3 Replies Latest reply on Dec 30, 2010 8:07 AM by adamw

    Set of Enum containing wrong data

    vincenzo.vitale

      Hi,

      in my main POJO I have a set of enum like this:

       

      @Audited

      @Entity

      @Table(name = "API_KEYS")

      public final class ApiKey extends AbstractBusinessObject implements Serializable {

       

      /**

      * The key id.

      */

      @Id

      @GeneratedValue(strategy = GenerationType.AUTO)

      private Long id;

       

      /**

      * The key value.

      */

      @Column(unique = true)

      private String value;

       

      @ElementCollection(targetClass

      = ServiceName.class, fetch = FetchType.EAGER)

      @CollectionTable(name = "SERVICE_NAMES", joinColumns = @JoinColumn(name = "API_KEY_ID"))
      @Column(name = "SERVICE_ID")
      @AuditJoinTable(name = "SERVICE_NAMES_AUD", inverseJoinColumns = @JoinColumn(name = "API_KEY_ID"))
      // IMPORTANT: Absolutely fundamental here to not use Collection or just Set
      // because otherwise Hibernate - our friend - will return a PersistentBag
      // object which doesn't implement equals in a java api fashion (comparing
      // the object of the collections) but just delegates to Object.eqauls().
      // Using Set, Hibernate - always our friend - will use PersistentSet which
      // implements equals() and hashcode() in the normal way.
      private Set<ServiceName> serviceNames = new HashSet<ServiceName>();

      @ElementCollection(targetClass = ServiceName.class, fetch = FetchType.EAGER)

      @CollectionTable(name = "SERVICE_NAMES", joinColumns = @JoinColumn(name = "API_KEY_ID"))

      @Column(name = "SERVICE_ID")

      @AuditJoinTable(name = "SERVICE_NAMES_AUD", inverseJoinColumns = @JoinColumn(name = "API_KEY_ID"))

      private Set<ServiceName> serviceNames = new HashSet<ServiceName>();

       

      }

       

      where:

       

      public enum ServiceName {

      PIPPO,

              PLUTO,

              PAPERINO;

      }

       

      When I persist, specifically when I remove a service name from the collection) my main entity in the db is correctly updated and it seems to me that also in the envers tables everything works fine looking at the revisions and what they are linking.

       

      BTW, the call to:

       

      AuditReader reader = AuditReaderFactory.get(getHibernateTemplate().getSessionFactory().getCurrentSession());

      ApiKey oldKey = reader.find(ApiKey.class, id, revision);

       

      returns wrong data and the set of service names still contains the elements I removed at that revision. spring is used for the transactionality and the class is annotated with:

      @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, readOnly = true)

       

      Am I doing something wrong or is this a known issue?

       

      My session factory (in spring):

       

      <bean id="sessionFactory"

              class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

              <property name="dataSource" ref="datasource" />

              <property name="eventListeners">

                  <map>

                      <entry key="post-insert">

                          <list>

                              <ref bean="apiKeyDbChangeListener" />

                              <ref bean="enversAuditEventListener" />

                          </list>

                      </entry>

                      <entry key="post-update">

                          <list>

                              <ref bean="apiKeyDbChangeListener" />

                              <ref bean="enversAuditEventListener" />

                          </list>

                      </entry>

                      <entry key="post-delete">

                          <list>

                              <ref bean="apiKeyDbChangeListener" />

                              <ref bean="enversAuditEventListener" />

                          </list>

                      </entry>

                      <entry key="pre-collection-update">

                          <list>

                              <!-- ref bean="apiKeyDbChangeListener" / -->

                              <ref bean="enversAuditEventListener" />

                          </list>

                      </entry>

                      <entry key="pre-collection-remove">

                          <list>

                              <!-- ref bean="apiKeyDbChangeListener" / -->

                              <ref bean="enversAuditEventListener" />

                          </list>

                      </entry>

                      <entry key="post-collection-recreate">

                          <list>

                              <!-- ref bean="apiKeyDbChangeListener" / -->

                              <ref bean="enversAuditEventListener" />

                          </list>

                      </entry>

                  </map>

              </property>

              <!-- We use the placeholder in order to load an hibernate test configuration

                  when running unit tests integrated with spring (like the one testing the

                  ApiKeyDbDao) -->

              <property name="configLocation"

                  value="classpath:/hibernate.${lbs.auth.hibernate.testprefix}cfg.xml" />

          </bean>

       

      and:

      <hibernate-configuration>

          <session-factory>

              <property name="dialect">org.hibernate.dialect.HSQLDialect</property>

              <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>

              <property name="connection.url">jdbc:hsqldb:mem:test</property>

              <property name="connection.username">sa</property>

              <property name="connection.password"></property>

       

              <!-- Only this at the moment works with Envers. -->

              <property name="hibernate.hbm2ddl.auto">update</property>

              <property name="connection.autoReconnect">true</property>

              <property name="connection.autoReconnectForPools">true</property>

              <property name="connection.is-connection-validation-required">true</property>

       

              <property name="org.hibernate.envers.audit_strategy">org.hibernate.envers.strategy.ValidityAuditStrategy</property>

       

              <mapping class="com.tomtom.lbs.auth.apikey.ApiKey" />

              <mapping class="com.tomtom.lbs.auth.apikey.ApiKeyRevisionEntity" />

          </session-factory>

      </hibernate-configuration>

       

      Thanks,

      Vincenzo.

        • 1. Re: Set of Enum containing wrong data
          vincenzo.vitale

          Debugging hibernate and envers I'm seeing that in the AuditEnversListener:

           

          only onPreRemoveCollection(PreCollectionRemoveEvent event) and onPostRecreateCollection(PostCollectionRecreateEvent event) got called but never onPreUpdateCollection(PreCollectionUpdateEvent event), I guess this could make sense since I'm updating the entity in a different session and it's completely a new object with the same id; probably the flow in hibernate is remove all the collection element and recreate the collection.

           

          But than when onPreRemoveCollection(PreCollectionRemoveEvent event) {

                  CollectionEntry collectionEntry = getCollectionEntry(event);

                  if (collectionEntry != null && !collectionEntry.getLoadedPersister().isInverse()) {

                      onCollectionAction(event, null, collectionEntry.getSnapshot(), collectionEntry);

                  }

          is executed nothing is really done since the collection in the event is null thus getCollectionEntry(event) return null.

           

          Trying to find a workaround.

          • 2. Re: Set of Enum containing wrong data
            vincenzo.vitale

            Using merge instead of saveOrUpdate in my dao fixed the problem.

            I was thinking was a problem in the auditreadear but in my case was most probably while persisting. Not sure if this is a bug or something I didn't configured correctly. Please advise.

             

             

            Ciao,

            V.

            • 3. Re: Set of Enum containing wrong data
              adamw

              Looks suspicous - at first I would say that saveOrUpdate should work as well - as long as a new entity isn't created.

              Maybe you could create an Envers test case, which demonstrates the problem?

               

              Adam