Envers fails when an entity has dynamic-component

Description

hi,

Am working with hibernate version 3.6.0.Final. Envers fails during initialization when an entity has dynamic-component.

Hibernate.cfg.xml -
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/company?createDatabaseIfNotExist=true&amp;useUnicode=true&amp;characterEncoding=utf-8</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>

<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>

</session-factory>
</hibernate-configuration>

There is only 1 entity in this test case -
Employee.java:

import ...
@Audited
public class Employee implements java.io.Serializable {

private Long id;
private String name ;
@NotAudited
private Map customProperties = new HashMap(); // maps to dynamic-component
.. getter/setter ...

Hibernate mapping (using xml as dynamic-component is not available as annotation)
<hibernate-mapping default-access="property">

<class name="com.company.domain.Employee" table="EMPLOYEE">

<id name="id" column="id">
<generator class="native"/>
</id>

<property name="name" type="string" column="name" length="255" not-null="false"/>

<dynamic-component insert="true" name="customProperties" optimistic-lock="true" unique="false" update="true" >
</dynamic-component>

</class>

</hibernate-mapping>

HibernateUtil -
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
AuditEventListener auditEventListener = new AuditEventListener();
Configuration configuration = new Configuration();
configuration.getEventListeners().setPostInsertEventListeners(new PostInsertEventListener[]{auditEventListener});
configuration.getEventListeners().setPostUpdateEventListeners(new PostUpdateEventListener[]{auditEventListener});
configuration.getEventListeners().setPostDeleteEventListeners(new PostDeleteEventListener[]{auditEventListener});
return configuration.configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}

Main class -
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );

App app = new App();
app.createAndStore();
app.createAndStoreVersion();
HibernateUtil.getSessionFactory().close();
}

private void createAndStore() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();

Employee emp = new Employee();
emp.setName("name");
session.save(emp);
session.getTransaction().commit();
}

private void createAndStoreVersion() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();

Employee emp = (Employee) session.get(Employee.class, 1L);
emp.setName("updated name");
session.save(emp);
session.getTransaction().commit();
}

}

Initialization fails, log is:
Caused by: java.lang.NullPointerException
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at org.hibernate.annotations.common.util.ReflectHelper.classForName(ReflectHelper.java:143)
at org.hibernate.annotations.common.reflection.java.JavaReflectionManager.classForName(JavaReflectionManager.java:117)
at org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader$ComponentPropertiesSource.<init>(AuditedPropertiesReader.java:269)
at org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader$ComponentPropertiesSource.<init>(AuditedPropertiesReader.java:263)
at org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader.addFromProperties(AuditedPropertiesReader.java:105)
at org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader.addPropertiesFromClass(AuditedPropertiesReader.java:89)
at org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader.read(AuditedPropertiesReader.java:67)
at org.hibernate.envers.configuration.metadata.reader.AnnotationsMetadataReader.getAuditData(AnnotationsMetadataReader.java:114)
at org.hibernate.envers.configuration.EntitiesConfigurator.configure(EntitiesConfigurator.java:80)
at org.hibernate.envers.configuration.AuditConfiguration.<init>(AuditConfiguration.java:97)
at org.hibernate.envers.configuration.AuditConfiguration.getFor(AuditConfiguration.java:129)
at org.hibernate.envers.event.AuditEventListener.initialize(AuditEventListener.java:335)
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)
... 6 more

After some debugging I found that a dynamic-component doesn't have class attribute (it is always java.util.HashMap), envers tries to look it up and fails.

Secondly, I coudln't figure out how to attach envers listeners via hibernate.cfg.xml, so used run time configuration.
All the three approaches failed -
1. <property name="hibernate.ejb.event.post-insert">org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener</property>
2. <listener class="org.hibernate.envers.event.AuditEventListener" type="post-insert"/> (DTD validation fails)
3. <event type="post-insert">
<listener class="org.hibernate.envers.event.AuditEventListener"/>
</event> (DTD validation fails).

Also tracked in forum - https://forum.hibernate.org/viewtopic.php?f=1&t=1009975

Junit to reproduce this scenario is attached. Execute App.java, you'll need mysql.

regards,
Rajeev

Environment

Windows 7, java version "1.6.0_18" (32 bit), hibernate-core-3.6.0.Final, mysql-5.1.45-win32

Status

Assignee

AdamA

Reporter

Rajeev

Fix versions

None

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

Affirmative

Pull Request

None

backportDecision

None

Components

Affects versions

3.6.0

Priority

Major
Configure