6 Replies Latest reply on Oct 3, 2009 1:52 PM by adamw

    interesting behaviour and compatibility question

    skomarla

      Hi.

      I am researching using Envers for our audit requirements and it has been very helpful so far. I've run into something "interesting" and I'm not sure if this is expected or not.

      env:
      Hibernate 3.3.2.GA
      Envers 1.2.1.GA-hibernate-3.3
      Spring Framework 2.5.6.. though not really important for this post.

      Here is my use code..:

      // dummied down version of class for illustration
      @Audited
      public class Node {
       private Integer id;
       private Integer objectVersion;
      
       @Audited
       private NodeStatus status;
      
       @Audited
       private Fee fee;
      
       private String name;
       private String description;
       private Date createDate;
       private Date statusDate;
      
       // snip getters setters and other attrs
      }
      
      // Node Status is
      public class NodeStatus extends Status {
       private static Map<String,NodeStatus> statusMap = new HashMap<String,NodeStatus>();
       private static final String ACTIVE="Active";
       private static final String SUSPENDED="Suspended";
       private static final String INACTIVE="Inactive";
      
       static {
       statusMap.put(INACTIVE, new NodeStatus(15, INACTIVE));
       statusMap.put(ACTIVE, new NodeStatus(16, ACTIVE));
       statusMap.put(SUSPENDED, new NodeStatus(17, SUSPENDED));
       }
      
       NodeStatus() {
       super();
       }
       private NodeStatus(Integer id, String name) {
       super(id, name);
       }
       public static NodeStatus Active() {
       return statusMap.get(ACTIVE);
       }
       public static NodeStatus Inactive() {
       return statusMap.get(INACTIVE);
       }
       public static NodeStatus Suspended() {
       return statusMap.get(SUSPENDED);
       }
      
       // snip hashCode, equals, toString etc.
      }
      
      public abstract class Status extends Base implements LookUp {
      
       public Status() {
       super();
       }
      
       public Status(Integer id, String name) {
       super();
       this.id = id;
       this.name = name;
       }
       private Integer id;
       private String name;
       private String description;
      
       // getters + setters snipped
      }
      
      // Base is an extention of LightEntity from Gilead (pretty nice toolset)
      public abstract class Base extends LightEntity implements Serializable {
       // nothing here for now
      }
      
      // Lookup is a marker interface for other use...
      /**
       * Interface for a domain object that is mapped to a Look Up table
       */
      public interface LookUp {
       public Integer getId();
       public String getName();
      }
      
      
      public class Fee implements Serializable{
       private static final long serialVersionUID = -1895794651654982575L;
      
       private Float value;
       private String unit;
      
       public Fee() {}
       public Fee(Float value, String unit) {
       setUnit(unit);
       setValue(value);
       }
      
       // snip hashCode, equals, toString etc.
      }
      


      now the mapping (done in xml)
      <?xml version="1.0"?>
      <!DOCTYPE hibernate-mapping PUBLIC
       "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
       "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
      
      <hibernate-mapping default-access="field">
       <class name="com.sterling.ifn.model.Node" table="IFN_NODE">
       <id name="id" type="int">
       <column name="IFN_Node_ID"/>
       <generator class="identity"/>
       </id>
       <version name="objectVersion" type="int">
       <column name="Row_Version" not-null="true"/>
       </version>
       <component name="fee">
       <property name="value" type="float">
       <column name="Node_Fee"/>
       </property>
       <property name="unit" type="UnitOfTimeType">
       <column name="Fee_Unit_ID"/>
       </property>
       </component>
       <many-to-one name="status" fetch="select"
       class="com.sterling.ifn.model.NodeStatus">
       <column name="IFN_Status_ID"/>
       </many-to-one>
       <property name="name" type="string">
       <column name="Node_NME" length="128" unique="true"/>
       </property>
       <property name="description" type="string">
       <column name="Node_Desc" length="256"/>
       </property>
       <property name="createDate" type="timestamp">
       <column name="Create_DTE" length="23"/>
       </property>
       <property name="statusDate" type="timestamp">
       <column name="Status_DTE" length="23"/>
       </property>
       </class>
      </hibernate-mapping>
      


      Running a unit test with the above.. I get the expected exception

      
      Caused by: org.hibernate.MappingException: An audited relation to a non-audited entity com.sterling.ifn.model.NodeStatus!
       at org.hibernate.envers.configuration.metadata.ToOneRelationMetadataGenerator.addToOne(ToOneRelationMetadataGenerator.java:59)
       at org.hibernate.envers.configuration.metadata.AuditMetadataGenerator.addValue(AuditMetadataGenerator.java:126)
       at org.hibernate.envers.configuration.metadata.AuditMetadataGenerator.addProperties(AuditMetadataGenerator.java:161)
       at org.hibernate.envers.configuration.metadata.AuditMetadataGenerator.generateSecondPass(AuditMetadataGenerator.java:405)
       at org.hibernate.envers.configuration.EntitiesConfigurator.configure(EntitiesConfigurator.java:96)
       at org.hibernate.envers.configuration.AuditConfiguration.<init>(AuditConfiguration.java:86)
       at org.hibernate.envers.configuration.AuditConfiguration.getFor(AuditConfiguration.java:99)
       at org.hibernate.envers.event.AuditEventListener.initialize(AuditEventListener.java:260)
       at org.hibernate.event.EventListeners$1.processListener(EventListeners.java:198)
       at org.hibernate.event.EventListeners.processListeners(EventListeners.java:181)
       at org.hibernate.event.EventListeners.initializeListeners(EventListeners.java:194)
       ... 58 more
      
      


      I add @Audited to NodeStatus definition to see it works out ok.. (but NodeStatus is not really an entity! there is no hibernate mapping for it.
      I get this exception..

      Caused by: org.hibernate.HibernateException: could not init listeners
       at org.hibernate.event.EventListeners.initializeListeners(EventListeners.java:205)
       at org.hibernate.cfg.Configuration.getInitializedEventListeners(Configuration.java:1352)
       at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1341)
       at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
       at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:814)
       at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:732)
       at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
       ... 50 more
      Caused by: java.lang.NullPointerException
       at org.hibernate.envers.configuration.metadata.AuditMetadataGenerator.generateInheritanceMappingData(AuditMetadataGenerator.java:305)
       at org.hibernate.envers.configuration.metadata.AuditMetadataGenerator.generateFirstPass(AuditMetadataGenerator.java:349)
       at org.hibernate.envers.configuration.EntitiesConfigurator.configure(EntitiesConfigurator.java:87)
       at org.hibernate.envers.configuration.AuditConfiguration.<init>(AuditConfiguration.java:86)
       at org.hibernate.envers.configuration.AuditConfiguration.getFor(AuditConfiguration.java:99)
       at org.hibernate.envers.event.AuditEventListener.initialize(AuditEventListener.java:260)
       at org.hibernate.event.EventListeners$1.processListener(EventListeners.java:198)
       at org.hibernate.event.EventListeners.processListeners(EventListeners.java:181)
       at org.hibernate.event.EventListeners.initializeListeners(EventListeners.java:194)
       ... 58 more
      


      Ok.. maybe I need to mark Base as @Audited. .. and it worked, but I ended up with a IFN_STATUS_AUD table which is not very ideal, especially since it is just a look up table, and will never get updated

      So, I have two things I need confirmation about:

      1) I didn't have to annotate the Fee class because it is just a component
      2) How come I didn't have to annotate Base (NodeStatus extends Status extends Base?


      Regarding why I needed to annotate NodeStatus, it is probably related to the discussion in this thread:
      http://www.jboss.org/index.html?module=bb&op=viewtopic&t=157846&postdays=0&postorder=asc&start=10

      Ideally, I would not annotate the NodeStatus class, but only annotate the association.. So, I see that a patch was submitted and applied to 3.5.0.Beta-1. So will envers from 3.5.0.Beta-1 be compatible with Hibernate 3.3.2?
      If so, I can give that a go.

      Thanks for the great work!


        • 1. Re: interesting behaviour and compatibility question
          adamw

          Well, so what is NodeStatus if it's not an entity? In an audited entity, for every relation that is audited, the target entity must be audited, or the relation must be explicitly marked as targeting a non-audited entity (this feature is new in trunk) - as is the case for look-up tables.

          There is a Hibernate-3.3 compatible branch of the 3.5 code, with all updates and fixes:
          http://anonsvn.jboss.org/repos/hibernate/core/branches/envers-hibernate-3.3/

          Adam

          • 2. Re: interesting behaviour and compatibility question
            skomarla

            Thanks..

            Where can I get hibernate-parent 3.5.0-SNAPSHOT?

            http://repository.jboss.org/maven2/org/hibernate/hibernate-parent/ only has 3.5.0.Beta-1/ 19-Aug-2009 22:54 -

            • 3. Re: interesting behaviour and compatibility question
              skomarla

              Regarding NodeStatus..you are correct. I did find a mapping for Status, where NodeStatus is listed as a subclass.. so ofcourse, that makes it an entity

              • 4. Re: interesting behaviour and compatibility question
                adamw

                Do you mean that you cannot build it once you've downloaded it?

                Adam

                • 5. Re: interesting behaviour and compatibility question
                  skomarla

                  Correct.. when i use the pom that is checked in, my maven cannot locate hibernate-parent 3.5.0-SNAPSHOT.. exact message:

                  [INFO] Scanning for projects...
                  [INFO] -----------------------------------------------------
                  [ERROR] FATAL ERROR
                  [INFO] -----------------------------------------------------
                  [INFO] Failed to resolve artifact.
                  
                  GroupId: org.hibernate
                  ArtifactId: hibernate-parent
                  Version: 3.5.0-SNAPSHOT
                  
                  Reason: Unable to download the artifact from any repository
                  
                   org.hibernate:hibernate-parent:pom:3.5.0-SNAPSHOT
                  
                  from the specified remote repositories:
                   central (http://repo1.maven.org/maven2)


                  I changed it to 3.5.0.Beta-1 but also needed to specify a plugin repository.. basically, this is the patch I made locally, but not really submitting it to you since its probably not necessary to be checked in.

                  Index: pom.xml
                  ===================================================================
                  --- pom.xml (revision 17577)
                  +++ pom.xml (working copy)
                  @@ -7,7 +7,7 @@
                   <parent>
                   <groupId>org.hibernate</groupId>
                   <artifactId>hibernate-parent</artifactId>
                  - <version>3.5.0-SNAPSHOT</version>
                  + <version>3.5.0.Beta-1</version>
                   </parent>
                  
                   <groupId>org.jboss.envers</groupId>
                  @@ -64,6 +64,26 @@
                   </plugins>
                   </build>
                  
                  + <repositories>
                  + <repository>
                  + <id>jboss</id>
                  + <name>JBoss Repository</name>
                  + <url>http://repository.jboss.org/maven2</url>
                  + </repository>
                  + </repositories>
                  + <pluginRepositories>
                  + <pluginRepository>
                  + <id>jboss-plugin</id>
                  + <url>http://repository.jboss.org/maven2</url>
                  + <releases>
                  + <enabled>true</enabled>
                  + </releases>
                  + <snapshots>
                  + <enabled>true</enabled>
                  + </snapshots>
                  + </pluginRepository>
                  + </pluginRepositories>
                  +
                   <dependencies>
                   <dependency>
                   <groupId>org.hibernate</groupId>
                  


                  • 6. Re: interesting behaviour and compatibility question
                    adamw

                    Thanks, I updated the pom.xml.

                    The additional repositories are a standard thing that you must have when building any Hiberante module.

                    Adam