2010-11-22 48 views

回答

3

基本上,事件的参与取决于事件(侦听器)的类型和获取数据的方式。虽然你是对的,事实上在使用实体查询(EntityManager.find ...,Session.load ...)和使用HQL查询之间显然是有区别的。但是,如果HQL查询用作命名查询或用作HQL常规查询,则无关紧要。

为了说明这一点,我使用Spring Boot,Spring Data JPA和Hibernate创建了一个小例子,因为使用该组合很容易。这些效果与使用Hibernate的其他应用程序体系结构类似。

如果我们使用加载事件,例如,如果使用EntityManager.find/Session.load接收实体,则直接加载的实体仅传播LoadEvent类型的事件。使用HQL(无论是Spring Data JPA还是纯Hibernate),如果可疑实体间接作为关联获取,则只有该特定类型的事件。与此相反,PostLoadEvent在每种情况下都被触发。

下面是一些简单的类来试试这个。

InterceptionByEventCandidate(这个实体,我们要拦截):

package com.example.model; 

import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 

@Entity 
public class InterceptionByEventCandidate { 

    public static final String FETCH_ALL_QUERY = "SELECT c FROM InterceptionByEventCandidate c"; 
    public static final String FETCH_ALL_NAMED_QUERY = "selectAll"; 

    @Id 
    @GeneratedValue 
    private long id; 

    private boolean interceptedOnLoad; 

    private boolean interceptedOnPostLoad; 

    public long getId() { 
     return id; 
    } 

    public boolean isInterceptedOnLoad() { 
     return interceptedOnLoad; 
    } 

    public void setInterceptedOnLoad(boolean interceptedOnLoad) { 
     this.interceptedOnLoad = interceptedOnLoad; 
    } 

    public boolean isInterceptedOnPostLoad() { 
     return interceptedOnPostLoad; 
    } 

    public void setInterceptedOnPostLoad(boolean interceptedOnPostLoad) { 
     this.interceptedOnPostLoad = interceptedOnPostLoad; 
    } 

} 

CandidateHost(需要主机证明关联抓取):

package com.example.model; 

import javax.persistence.CascadeType; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.OneToOne; 

@Entity 
public class CandidateHost { 

    public static final String FETCH_ALL_QUERY = "SELECT h FROM CandidateHost h JOIN h.candidate c"; 
    public static final String FETCH_ALL_NAMED_QUERY = "selectAllHosts"; 

    @Id 
    @GeneratedValue 
    private long id; 

    @OneToOne(cascade = CascadeType.PERSIST) 
    private InterceptionByEventCandidate candidate; 

    public InterceptionByEventCandidate getCandidate() { 
     return candidate; 
    } 

    public void setCandidate(InterceptionByEventCandidate candidate) { 
     this.candidate = candidate; 
    } 

} 

现在,我们需要一个适当的监听器,为方便起见,我们在一个HQLEventListener中组合两种提及的事件类型:

package com.example.event; 

import org.hibernate.HibernateException; 
import org.hibernate.event.spi.LoadEvent; 
import org.hibernate.event.spi.LoadEventListener; 
import org.hibernate.event.spi.PostLoadEvent; 
import org.hibernate.event.spi.PostLoadEventListener; 
import org.springframework.stereotype.Component; 

import com.example.model.InterceptionByEventCandidate; 

@Component 
public class HqlEventListener implements LoadEventListener, PostLoadEventListener { 

    private static final long serialVersionUID = -7248393324424903264L; 

    @Override 
    public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException { 
     final Object object = event.getResult(); 
     if (object instanceof InterceptionByEventCandidate) { 
      ((InterceptionByEventCandidate) object).setInterceptedOnLoad(true); 
     } 
    } 

    @Override 
    public void onPostLoad(PostLoadEvent event) { 
     final Object object = event.getEntity(); 
     if (object instanceof InterceptionByEventCandidate) { 
      ((InterceptionByEventCandidate) object).setInterceptedOnPostLoad(true); 
     } 
    } 

} 

告诉Hibernate,我们要处理,我们需要一些配置哪些事件:

package com.example.event; 

import javax.annotation.PostConstruct; 
import javax.persistence.EntityManagerFactory; 
import javax.persistence.PersistenceUnit; 

import org.hibernate.event.service.spi.EventListenerRegistry; 
import org.hibernate.event.spi.EventType; 
import org.hibernate.internal.SessionFactoryImpl; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Configuration; 

@Configuration 
public class EntityListenerConfiguration { 

    @PersistenceUnit 
    private EntityManagerFactory entityManagerFactory; 

    @Autowired 
    private HqlEventListener hqlEventListener; 

    @PostConstruct 
    protected void init() { 
     final SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class); 
     final EventListenerRegistry eventListenerRegistry = sessionFactory.getServiceRegistry() 
       .getService(EventListenerRegistry.class); 

     eventListenerRegistry.getEventListenerGroup(EventType.LOAD).appendListener(hqlEventListener); 
     eventListenerRegistry.getEventListenerGroup(EventType.POST_LOAD).appendListener(hqlEventListener); 

    } 
} 

提供和重用我们存储它的命名查询在包信息文件:

@NamedQueries({ 
     @NamedQuery(name = InterceptionByEventCandidate.FETCH_ALL_NAMED_QUERY, query = InterceptionByEventCandidate.FETCH_ALL_QUERY), 
     @NamedQuery(name = CandidateHost.FETCH_ALL_NAMED_QUERY, query = CandidateHost.FETCH_ALL_QUERY) }) 

package com.example.event; 

import org.hibernate.annotations.NamedQueries; 
import org.hibernate.annotations.NamedQuery; 

import com.example.model.CandidateHost; 
import com.example.model.InterceptionByEventCandidate; 

,当然还有如果它们显示相同的行为,则需要一些Spring Data JPA存储库来尝试。

InterceptionByEventCandidateRepository:

package com.example.repository; 

import java.util.Set; 

import org.springframework.data.jpa.repository.Query; 
import org.springframework.data.repository.CrudRepository; 
import org.springframework.stereotype.Repository; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.model.InterceptionByEventCandidate; 

@Repository 
public interface InterceptionByEventCandidateRepository extends CrudRepository<InterceptionByEventCandidate, Long> { 

    @Query(InterceptionByEventCandidate.FETCH_ALL_QUERY) 
    @Transactional(readOnly = true) 
    Set<InterceptionByEventCandidate> findByHQL(); 

    @Query(name = InterceptionByEventCandidate.FETCH_ALL_NAMED_QUERY) 
    @Transactional(readOnly = true) 
    Set<InterceptionByEventCandidate> findByNamedHQL(); 
} 

CandidateHostRepository:

package com.example.repository; 

import java.util.Set; 

import org.springframework.data.jpa.repository.Query; 
import org.springframework.data.repository.CrudRepository; 
import org.springframework.stereotype.Repository; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.model.CandidateHost; 

@Repository 
public interface CandidateHostRepository extends CrudRepository<CandidateHost, Long> { 

    @Query(CandidateHost.FETCH_ALL_QUERY) 
    @Transactional(readOnly = true) 
    Set<CandidateHost> findByHQL(); 

    @Query(name = CandidateHost.FETCH_ALL_NAMED_QUERY) 
    @Transactional(readOnly = true) 
    Set<CandidateHost> findByNamedHQL(); 

} 

这是一个很大的示例代码,这里是(我也承认巨大的)测试,以检查事件之间有何不同策略:

package com.example.repository; 

import static org.hamcrest.CoreMatchers.is; 
import static org.junit.Assert.assertThat; 

import java.util.Set; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 

import org.hibernate.Query; 
import org.hibernate.Session; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.junit4.SpringRunner; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.model.CandidateHost; 
import com.example.model.InterceptionByEventCandidate; 

@RunWith(SpringRunner.class) 
@Transactional 
@SpringBootTest 
public class HqlEventTests { 

    @Autowired 
    private CandidateHostRepository candidateHostRepository; 

    @Autowired 
    private InterceptionByEventCandidateRepository interceptionByEventCandidateRepository; 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Before 
    public void setUp() { 
     final CandidateHost host = new CandidateHost(); 
     final InterceptionByEventCandidate interceptionByEventCandidate = new InterceptionByEventCandidate(); 
     host.setCandidate(interceptionByEventCandidate); 
     candidateHostRepository.save(host); 
     entityManager.flush(); 
     entityManager.clear(); 
    } 

    @Test 
    public void interceptCandidateAtFindingHostBySpringDataJpaHql() { 
     final Set<CandidateHost> result = candidateHostRepository.findByHQL(); 
     verifyInterceptions(result.stream().findFirst().get().getCandidate(), true, true); 
    } 

    @Test 
    public void interceptCandidateAtFindingHostBySpringDataJpaNamedHql() { 
     final Set<CandidateHost> result = candidateHostRepository.findByNamedHQL(); 
     verifyInterceptions(result.stream().findFirst().get().getCandidate(), true, true); 
    } 

    @Test 
    public void interceptCandidateAtFindingHostByHibernateHql() { 
     final Session session = entityManager.unwrap(Session.class); 
     final Query query = session.createQuery(CandidateHost.FETCH_ALL_QUERY); 

     verifyInterceptions(((CandidateHost) query.list().stream().findFirst().get()).getCandidate(), true, true); 
    } 

    @Test 
    public void interceptCandidateAtFindingHostByHibernateNamedHql() { 
     final Session session = entityManager.unwrap(Session.class); 
     final Query query = session.getNamedQuery(CandidateHost.FETCH_ALL_NAMED_QUERY); 

     verifyInterceptions(((CandidateHost) query.list().stream().findFirst().get()).getCandidate(), true, true); 
    } 

    @Test 
    public void interceptCandidateBySpringDataJpaHql() { 
     final Set<InterceptionByEventCandidate> result = interceptionByEventCandidateRepository.findByHQL(); 
     verifyInterceptions(result.stream().findFirst().get(), false, true); 
    } 

    @Test 
    public void interceptCandidateBySpringDataJpaNamedHql() { 
     final Set<InterceptionByEventCandidate> result = interceptionByEventCandidateRepository.findByNamedHQL(); 
     verifyInterceptions(result.stream().findFirst().get(), false, true); 
    } 

    @Test 
    public void interceptCandidateByHibernateHql() { 
     final Session session = entityManager.unwrap(Session.class); 
     final Query query = session.createQuery(InterceptionByEventCandidate.FETCH_ALL_QUERY); 

     verifyInterceptions((InterceptionByEventCandidate) query.list().stream().findFirst().get(), false, true); 
    } 

    @Test 
    public void interceptCandidateByHibernateNamedHql() { 
     final Session session = entityManager.unwrap(Session.class); 
     final Query query = session.getNamedQuery(InterceptionByEventCandidate.FETCH_ALL_NAMED_QUERY); 

     verifyInterceptions((InterceptionByEventCandidate) query.list().stream().findFirst().get(), false, true); 
    } 

    @Test 
    public void interceptCandidateByHibernateFind() { 
     long id = interceptionByEventCandidateRepository.findAll().iterator().next().getId(); 

     entityManager.flush(); 
     entityManager.clear(); 

     final Session session = entityManager.unwrap(Session.class); 
     final InterceptionByEventCandidate candidate = session.load(InterceptionByEventCandidate.class, id); 
     verifyInterceptions(candidate, true, true); 
    } 

    private void verifyInterceptions(final InterceptionByEventCandidate candidate, final boolean expectedLoadEventState, 
      final boolean expectedPostLoadEventState) { 
     assertThat("Expected load state did not match!", candidate.isInterceptedOnLoad(), is(expectedLoadEventState)); 
     assertThat("Expected postload state did not match!", candidate.isInterceptedOnPostLoad(), 
       is(expectedPostLoadEventState)); 
    } 
} 

看着这个测试我们可以看到重新命名和常规查询没有区别。如果你担心另一种类型的事件,我想这很容易扩展。