如何在 Hibernate JPA 中添加可编程的实体侦听器

Posted

技术标签:

【中文标题】如何在 Hibernate JPA 中添加可编程的实体侦听器【英文标题】:how to add entity listener programmable in Hibernate JPA 【发布时间】:2017-09-20 12:10:32 【问题描述】:

我用的是spring、hibernate、jpa2.1。 如下:

@Entity
@EntityListeners(DemoListener.class)
public class Demo 

    @Id
    private Long id;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    


public class DemoListener 

    @PersistenceContext
    private EntityManager entityManager;

    @PrePersist
    public void prePersist(Demo demo)

    

这个例子很好,当我想添加更多监听器时,我必须修改Demo实体,但Demo在其他jar中,我不想使用XML配置,有什么办法像这样:

...addListener(Demo.class, new DemoListener());
...addListener(Demo.class, new OtherDemoListener());

【问题讨论】:

【参考方案1】:

基于hibernate-orm docs 和hibernate-tutorials:

/**
 * @param <T> one of @link EventType#baseListenerInterface()
 * @see org.hibernate.event.service.spi.EventListenerRegistry
 */
public interface JpaEventListenerRegistry<T> 

    /**
     * add listener for entity class
     *
     * @param entityClass can't be null
     * @param listener    can't be null
     */
    void addListener(Class<?> entityClass, T listener);




public class JpaEventListenerRegistryImpl implements JpaEventListenerRegistry<Object> 

    private Logger logger = LoggerFactory.getLogger(getClass());

    private EventListenerRegistry eventListenerRegistry;
    private Map<EventType, JpaEventListenerRegistry> listeners = new HashMap<EventType, JpaEventListenerRegistry>();

    public JpaEventListenerRegistryImpl(EventListenerRegistry eventListenerRegistry) 
        this.eventListenerRegistry = eventListenerRegistry;
        initDefault();
    

    private void initDefault() 
        listeners.put(EventType.PRE_INSERT, new DomainPreInsertEventListener());
        listeners.put(EventType.POST_INSERT, new DomainPostInsertEventListener());
        for (Map.Entry<EventType, JpaEventListenerRegistry> entry : listeners.entrySet()) 
            eventListenerRegistry.appendListeners(entry.getKey(), entry.getValue());
        
    

    @SuppressWarnings("unchecked")
    public void addListener(Class<?> entityClass, Object listener) 
        logger.info("add listener  for entity ", listener, entityClass.getName());
        for (EventType eventType : EventType.values()) 
            Class<?> listenerInterface = eventType.baseListenerInterface();
            if (listenerInterface.isAssignableFrom(listener.getClass())) 
                JpaEventListenerRegistry registry = listeners.get(eventType);
                if (registry == null) 
                    logger.warn("the event type  for listener  is not supported", eventType, listener);
                 else 
                    registry.addListener(entityClass, listener);
                
            
        
    

    public static class Abstract<T> implements JpaEventListenerRegistry<T> 

        private Logger logger = LoggerFactory.getLogger(getClass());
        private Map<Class<?>, List<T>> listeners = new HashMap<Class<?>, List<T>>();

        public void addListener(Class<?> entityClass, T listener) 
            logger.info("add listener  for entity ", listener, entityClass.getName());
            List<T> listeners = this.listeners.get(entityClass);
            if (listeners == null) 
                listeners = new ArrayList<T>();
                this.listeners.put(entityClass, listeners);
            
            listeners.add(listener);
        

        List<T> findListener(Class<?> entityClass) 
            for (Map.Entry<Class<?>, List<T>> entry : listeners.entrySet()) 
                if (entry.getKey().isAssignableFrom(entityClass)) 
                    return entry.getValue();
                
            
            return null;
        

    


    public static class DomainPreInsertEventListener extends Abstract<PreInsertEventListener> implements PreInsertEventListener 

        public boolean onPreInsert(PreInsertEvent event) 
            return onPreInsert(event, findListener(event.getEntity().getClass()));
        

        private boolean onPreInsert(PreInsertEvent event, List<PreInsertEventListener> listeners) 
            if (listeners == null) return false;
            for (PreInsertEventListener listener : listeners) 
                if (listener.onPreInsert(event)) return true;
            
            return false;
        
    

    public static class DomainPostInsertEventListener extends Abstract<PostInsertEventListener> implements PostInsertEventListener 

        public void onPostInsert(PostInsertEvent event) 
            onPostInsert(event, findListener(event.getEntity().getClass()));
        

        private void onPostInsert(PostInsertEvent event, List<PostInsertEventListener> listeners) 
            if (listeners == null) return;
            for (PostInsertEventListener listener : listeners) 
                listener.onPostInsert(event);
            
        


        public boolean requiresPostCommitHanding(EntityPersister persister) 
            return false;
        
    



public class EntityManagerIllustrationTest extends TestCase 
    private EntityManagerFactory entityManagerFactory;

    @Override
    protected void setUp() throws Exception 
        // like discussed with regards to SessionFactory, an EntityManagerFactory is set up once for an application
        //      IMPORTANT: notice how the name here matches the name we gave the persistence-unit in persistence.xml!
        entityManagerFactory = Persistence.createEntityManagerFactory("org.hibernate.tutorial.jpa");

        SessionFactoryImplementor sessionFactory = entityManagerFactory.unwrap(SessionFactoryImplementor.class);
        EventListenerRegistry eventListenerRegistry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        JpaEventListenerRegistryImpl jpaEventListenerRegistry = new JpaEventListenerRegistryImpl(eventListenerRegistry);
        jpaEventListenerRegistry.addListener(EventListener.class, new JpaEventListener());
    

    private static class JpaEventListener implements PreInsertEventListener, PostInsertEventListener 
        public boolean onPreInsert(PreInsertEvent event) 
            Event entity = (Event) event.getEntity();
            System.out.println("onPreInsert:" + entity);
            return false;
        

        public void onPostInsert(PostInsertEvent event) 
            Event entity = (Event) event.getEntity();
            System.out.println("onPostInsert:" + entity);
        

        public boolean requiresPostCommitHanding(EntityPersister persister) 
            return false;
        
    

    @Override
    protected void tearDown() throws Exception 
        entityManagerFactory.close();
    

    public void testBasicUsage() 
        // create a couple of events...
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(new Event("Our very first event!", new Date()));
//        entityManager.persist(new Event("A follow up event", new Date()));
        entityManager.getTransaction().commit();
        entityManager.close();

        // now lets pull events from the database and list them
        entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        List<Event> result = entityManager.createQuery("from Event", Event.class).getResultList();
        for (Event event : result) 
            System.out.println("Event (" + event.getDate() + ") : " + event.getTitle());
        
        entityManager.getTransaction().commit();
        entityManager.close();
    



【讨论】:

以上是关于如何在 Hibernate JPA 中添加可编程的实体侦听器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Hibernate 为 Spring data JPA 的所有查找方法添加全局 where 子句?

如何在 JPA Hibernate 映射中将 GUID(不是 PK)添加到已经具有 PK(整数)的现有实体

如何强制初始化 Hibernate JPA 代理以在 JSON 调用中使用它

JPA,Hibernate:在现有工作 DTO 中添加新变量时“无法在类上找到适当的构造函数”

如何使用 JPA 和 Hibernate 连接两个不相关的实体

如何配置 Hibernate 以在 Karaf JPA 示例中工作?