-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: Event listener Found Two representations of same collection
PostPosted: Mon Aug 13, 2012 11:33 am 
Newbie

Joined: Mon Aug 13, 2012 11:27 am
Posts: 1
The following unit test demonstrates this problem. I have also included 3 solutions to the problem in the test. I have considered the existing comments and solutions posted on this stack trace but I believe this case could be different. I am looking for an explanation as to why the second test fails "testGetEntitiesInListenerQueryIt".

Code:
import static org.junit.Assert.assertEquals;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.Transaction;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.internal.DefaultPostLoadEventListener;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

public class EventListenerTest {

   private static ServiceRegistryImplementor serviceRegistry;
   private static SessionFactory sessionFactory;
   private static MyEntityListener myListener;

   private static DataSource createDataSource(String url, String driver, String userName, String password) {
      final BasicDataSource dataSource = new BasicDataSource();
      dataSource.setDriverClassName(driver);      
      dataSource.setUsername(userName);
      dataSource.setPassword(password);      
      dataSource.setUrl(url);
      return dataSource;
   }
   
   @BeforeClass
   public static void setup() {
      final DataSource dataSource = createDataSource("jdbc:hsqldb:mem:test", "org.hsqldb.jdbcDriver", "sa", "");

      final Configuration config = new Configuration();
      config.getProperties().put(AvailableSettings.DIALECT, "org.hibernate.dialect.HSQLDialect");
      config.getProperties().put(AvailableSettings.SHOW_SQL, "false");
      config.getProperties().put(AvailableSettings.FORMAT_SQL, "true");
      config.getProperties().put(AvailableSettings.HBM2DDL_AUTO, "create");
      config.getProperties().put(AvailableSettings.MAX_FETCH_DEPTH, "1");
      config.getProperties().put(Environment.DATASOURCE, dataSource);
      config.getProperties().put(AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS, "org.hibernate.context.internal.ThreadLocalSessionContext");
      
      config.addAnnotatedClass(Entity1.class);
      config.addAnnotatedClass(Entity2.class);
      config.addAnnotatedClass(Entity3.class);
      
      serviceRegistry = (ServiceRegistryImplementor) new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry();
      sessionFactory = (SessionFactoryImplementor) config.buildSessionFactory( serviceRegistry );

      final EventListenerRegistry registry = ((SessionFactoryImplementor) sessionFactory).getServiceRegistry().getService(EventListenerRegistry.class);
      
      myListener = new MyEntityListener();
       registry.getEventListenerGroup(EventType.POST_LOAD).appendListener(myListener);
      
       populateHsqlDb();
   }
   
   private static void populateHsqlDb() {
      final Session session = sessionFactory.getCurrentSession();
      final Transaction transaction = session.beginTransaction();
      
      final Entity1 entity1 = new Entity1("Entity1");
      entity1.setId(Long.valueOf(1));
      
      final Entity2 entity2a = new Entity2("Entity2a");      
      entity2a.setId(Long.valueOf(2));
      Entity2 entity2b = new Entity2("Entity2b");
      entity2b.setId(Long.valueOf(3));
      
      entity2a.setParentEntity(entity1);
      entity2b.setParentEntity(entity1);

      final Set<Entity2> entity2s = new HashSet<Entity2>();
      entity2s.add(entity2a);
      entity2s.add(entity2b);
      entity1.setEntity2s(entity2s);
      
      entity1.setEntity2s(entity2s);
      
      session.save(entity1);
      //session.save(entity2a);
      //session.save(entity2b);
      
      final Entity3 entity3 = new Entity3("Entity3 - unrelated.");
      entity3.setId(Long.valueOf(4));
      session.save(entity3);
      
      transaction.commit();
   }   
   
   //@Ignore
   @Test
   public void testGetEntitiesInListenerLoadIt() {
      Transaction transaction = null;
      
      try {
         final Session session = sessionFactory.getCurrentSession();
         transaction = session.beginTransaction();
         myListener.setLoadStrategy(new JustLoadItStrategy());
         
         final Long id = Long.valueOf(1);
         final Entity1 entity1 = (Entity1) sessionFactory.getCurrentSession().load(EventListenerTest.Entity1.class, id);
         assertEquals(2, entity1.getEntity2s().size());
      } finally {      
         transaction.rollback();
      }
   }

   @Ignore
   @Test
   public void testGetEntitiesInListenerQueryIt() {
      Transaction transaction = null;
      try {
         final Session session = sessionFactory.getCurrentSession();
         transaction = session.beginTransaction();
   
         //this approach will trigger a flush in the event listener.
         myListener.setLoadStrategy(new TryQueryItStrategy());
         
         final Long id = Long.valueOf(1);
         final Entity1 entity1 = (Entity1) sessionFactory.getCurrentSession().load(EventListenerTest.Entity1.class, id);
         assertEquals(2, entity1.getEntity2s().size());
         
         
      } finally {
         transaction.rollback();
      }
   }   
   
   public interface EntityInterface {
      String getEntityDescription();
      Long getId();
   }
   
   @Entity
   @Table(name = "ENTITY1")
   public static class Entity1 implements Serializable, EntityInterface {

      private static final long serialVersionUID = -8262641590629153464L;

      private Long id;
      private Set<Entity2> entity2s = new HashSet<Entity2>();
      private String description;
      
      public Entity1() {         
      }

      public Entity1(String description) {   
         this.description = description;
      }

       @Id
       @Column(name = "ENTITY1_ID")
       public Long getId() {
           return id;
       }

       public void setId(Long id) {
          this.id = id;
       }
      
      @OneToMany(cascade={CascadeType.ALL}, mappedBy = "parentEntity")
      /**
       * Solution option 3.
       */
      //commenting out the join fetch mode also solves the problem but only because it impacts the auto flush logic
      //in retrieving the set.
      @Fetch(FetchMode.JOIN)
      public Set<Entity2> getEntity2s() {
         return entity2s;
      }
      
      public void setEntity2s(Set<Entity2> entity2s) {
         this.entity2s = entity2s;
      }

       @Column(name = "ENTITY1_DESCRIPTION")
      @Override
      public String getEntityDescription() {
         return description;
      }
      
       public void setEntityDescription(String description) {
          this.description = description;
       }
   }
   
   @Entity
   @Table(name = "ENTITY2")
   public static class Entity2 implements Serializable, EntityInterface {
      private static final long serialVersionUID = 7451097975241296549L;
      
      private Entity1 parentEntity;
       private Long id;
       private String description;
      
       public Entity2() {          
       }

       public Entity2(String description) {   
         this.description = description;
      }

       @Id
       @Column(name = "ENTITY2_ID")
       public Long getId() {
           return id;
       }
      
       public void setId(Long id) {
          this.id = id;
       }

      @ManyToOne(fetch=FetchType.LAZY, optional=false)
       @JoinColumn(name="PARENT_ENTITY1_ID", nullable=false)
       public Entity1 getParentEntity() {
          return parentEntity;
       }

       public void setParentEntity(Entity1 entity) {
          this.parentEntity = entity;
       }

       @Column(name = "ENTITY2_DESCRIPTION")
      @Override
      public String getEntityDescription() {
         return description;
      }
      
      public void setEntityDescription(String description) {
         this.description = description;
      }
   }

   @Entity
   @Table(name = "ENTITY3")
   public static class Entity3 implements Serializable, EntityInterface {
      private static final long serialVersionUID = 7451097975241296549L;
      
       private Long id;
      private String description;
      
       public Entity3() {          
       }

       public Entity3(String description) {   
         this.description = description;
      }

       @Id
       @Column(name = "ENTITY3_ID")
       public Long getId() {
           return id;
       }
      
       public void setId(Long id) {
          this.id = id;
       }

       @Column(name = "ENTITY3_DESCRIPTION")
      @Override
      public String getEntityDescription() {
         return description;
      }
      
      public void setEntityDescription(String description) {
         this.description = description;
      }
   }

   public static interface MyUnrelatedEntityLoadStrategy {
      Entity3 loadIt(PostLoadEvent event);
   }
   
   /**
    * Solution option1 - this approach works but only because it doesn't trigger the auto flush.
    */
   public static class JustLoadItStrategy implements MyUnrelatedEntityLoadStrategy {
      @Override
      public Entity3 loadIt(PostLoadEvent event) {
         return (Entity3) sessionFactory.getCurrentSession().load(EventListenerTest.Entity3.class, Long.valueOf(4));
      }      
   }
   
   public static class TryQueryItStrategy implements MyUnrelatedEntityLoadStrategy {

      @Override
      public Entity3 loadIt(PostLoadEvent event) {
         
         final Query query = sessionFactory.getCurrentSession().createQuery("select e from EventListenerTest$Entity3 e where e.id = ?");
         //this gets the same reference to the session as getCurrentSession
         //final Query query = event.getSession().createQuery("select e from EventListenerTest$Entity3 e where e.id = ?");

         /**
          * Solution option2 - the statless session works because it will avoid the event listener
          */
         //Does this mean it is simply unsafe to query in an event listener if querying triggers the auto flush?
         //StatelessSession session = event.getPersister().getFactory().openStatelessSession();
         //final Query query = session.createQuery("select e from EventListenerTest$Entity3 e where e.id = ?");
         
         query.setParameter(0, Long.valueOf(4));
         return (Entity3) query.list().get(0);
      }
      
   }
   
   public static class MyEntityListener extends DefaultPostLoadEventListener {

      private static final long serialVersionUID = -9019214503421635237L;
      private MyUnrelatedEntityLoadStrategy strategy;
      
      public MyEntityListener() {}
      
      public void setLoadStrategy(MyUnrelatedEntityLoadStrategy strategy) {
         this.strategy = strategy;
      }
      
      @Override
      public void onPostLoad(PostLoadEvent event) {
         super.onPostLoad(event);
         
         final Object entity = event.getEntity();
         if(EntityInterface.class.isInstance(entity)) {
            EntityInterface myEntity = (EntityInterface)entity;
            System.out.println("on post load: " + myEntity.getId() + " - " + myEntity.getEntityDescription());
         }
         
         final Entity3 entity3 = strategy.loadIt(event);
         System.out.println("loading entity in event listener: " + entity3.getId() + " - " + entity3.getEntityDescription());
      }            
   }
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.