Search code examples
jpacdientitylisteners

CDI with EntityListener and timing issue?


I'm trying to do this.

public class MyEntityListener {

    @PrePersist
    private void onPrePersist(final Object object) {
        // set object with value fetched via onPostConstruct
    }

    @PostConstruct
    private void onPostConstruct() {
        // fetch some value using entityManager
    }

    @PersistenceContext
    private EntityManager entityManager;
}
  1. When I persist and instance via EJB, the entityManager is different instance from that of the EJB.
  2. onPrePersist is executed (before or) regardless of postConstruct.

Is this normal?


Solution

  • Accessing CDI/IOC within entity lifecycle annotation and EntityListerns

    Its not good practice to access EntityManager with in @PrePersist method call backs with in EntityListeners, cause the annotation method call back (like @Prepersist) are not designed for accessing the CDI, IOC beans in nutshell. Instead use PostActionEventListener families like (PostCommitInsertEventListener, PostInsertEventListener)

    Provide a desired implementation of PostCommitInsertEventListener interface

    public class RootAwarePostInsertEventListener implements PostCommitInsertEventListener {
    
        @Override
        public void onPostInsert(PostInsertEvent postInsertEvent) {
        //All entity states changes, session, session factory information are accessible with PostInsertEvent which extended AbstractEvent
        ...
        }
    
        @Override
        public boolean requiresPostCommitHanding(EntityPersister entityPersister) {
        //to acivate the onPostInsert 
            return true;
        }
    
        @Override
        public void onPostInsertCommitFailed(PostInsertEvent postInsertEvent) {
        ...
        }
        
        //Dependency Injection
        ...
    }
    

    Register it to EventListenerRegistery, sequentially to the SessionFactoryImpl

    @Component
    public class HibernateListenerConfig  {
    
        ...
        
        
        @PersistenceUnit
        private EntityManager em;
    
    
        @PostConstruct
        protected void init() {
            SessionFactoryImpl sessionFactory = factory.unwrap(SessionFactoryImpl.class);
            EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
            registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT)
                    .appendListener(new RootAwarePostInsertEventListener(em));
        }
        
        ...
    }
    

    That's all you don't need to create the modified ORM and session/session factory contexts meaning all previous auto context bootstrapping will remain the same and will just be interrupted by adding this event listeners.

    • All transaction lifecycle and entity states are full accessible for preforming desired operations and accessing the CDI/IOC
    • OPTIMISTIC_FORCE_INCREMENT and many other locks can applied and database operations can be called in clean second cache manner.
    • With in entity lifecycle annotation method call backs there is no information of previous or new fields in other words the Oldstate and Newstate of entity are not accessible in same time, instead with this approach,EntityPersister, session and session factory components and data are accessible to cover all needs for ORM context.
    • On going Transaction life cycle can be fully managed with different kind of entity interceptors.
    • Interceptors are way less cost that entity lifecycle annotations