dbUnit in a Spring Environment with MSSQL

20. Oktober 2011 Keine Kommentare

Today I needed to export and import some data from a database for testing purposes. I do knew dbUnit already and this seemed to be the tool of choice. I stumbled about a couple of problems, because another example of the usage of dbUnit with the Spring Framework was using Oracle and furthermore the FlatXmlDataSet. I wanted to use the XmlDataSet, because this seems to be easier to maintain manually.

In the following I will try to show, how to integrate dbUnit into the project. First of all, I needed to put the Maven Plugin into place:

<dependencies>
        <dependency>
            <groupId>org.dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.4.8</version>
            <scope>test</scope>
        </dependency>
...
</dependencies>
...
<build>
   <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>dbunit-maven-plugin</artifactId>
                <version>1.0-beta-3</version>
                <!--jar file that has the jdbc driver -->
                <dependencies>
                    <dependency>
                        <groupId>net.sourceforge.jtds</groupId>
                        <artifactId>jtds</artifactId>
                        <version>${jtds.version}</version>
                    </dependency>
                </dependencies>

                <configuration>
                    <driver>${database.driver.classname}</driver>
                    <url>${database.url}</url>
                    <username>${database.user}</username>
                    <password>${database.password}</password>
                    <dataTypeFactoryName>org.dbunit.ext.mssql.MsSqlDataTypeFactory</dataTypeFactoryName>
                    <ordered>true</ordered>
                </configuration>
            </plugin>
...
</plugins>
</build>

After this I was able to call mvn dbunit:export, which gave me an export of the current datastructure inside the database. It will generate a file target/dbunit/export.xml, which could then be used in the TestCases (we are using JUnit).

The TestCases are now looking something like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/META-INF/spring/testAC.xml"})
public class ServiceTest {
...
	@Autowired
	private DataSource dataSource;

Here we are autowiring the dataSource from the Application Context, this is needed to extract all necessary information for the database connection of dbUnit.

	// the following methods do make sure, that the database is setup correctly with
	// dbUnit
	private IDatabaseConnection getConnection() throws Exception {
    	// get connection
        Connection con = dataSource.getConnection();
        DatabaseMetaData databaseMetaData = con.getMetaData();
        IDatabaseConnection connection = new DatabaseConnection(con);
        DatabaseConfig config = connection.getConfig();
        config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MsSqlDataTypeFactory());
        return connection;
	}

Here we are making sure, that the connection is setup correctly and that the right DataTypeFactory is used. This should be the same factory as used in the pom.xml (see above).

	private IDataSet getDataSet() throws IOException, DataSetException {
		File file = new File("src/test/resources/dbexport.xml");
		assertTrue(file.exists());
		Reader reader = new FileReader(file);
		return new XmlDataSet(reader);
	}

Fetching the dataset from a file and use it in the DatabaseOperation of dbUnit.

	@Test
	@Transactional
	public void testDelete() throws Exception {
        DatabaseOperation.CLEAN_INSERT.execute(getConnection(), getDataSet());
		EntityVersionId id = new VersionId("9a5a8eb1f02b4e06ba9117a771f2b69c", 2L);
		Entity entity = this.entityService.find(id);
		assertNotNull(entity);
		this.entityService.delete(id);
	}

Please note, that we are using a special EntityVersionId, which is part of our Framework and contains two values. This is a combined ID. The usual ID is an UUID (String) and a “version” of type long. I guess, you will most probably not use something like this in your project.

Thats it, now everything works like expected ;-)

KategorienJava, Projects Tags:

Getting into CXF

9. Oktober 2011 Keine Kommentare

For my new Job (oh, i did not blog about this one, but more information will follow soon) I am currently investigating a couple of EAI frameworks. One of them is Apache CXF. For this investigation, I am implementing a very easy task and use an easy tutorial (Creating a REST service with CXF and Spring in 10 minutes. Well, to be totally honest, it took me more then 10 minutes to get this up and running. Of course, this is mainly due to the fact, that I wanted to gather some more information about JXF and used my own services.
The first issue I stumbled upon was a problem with the error message: “no resource classes found”. Problem here was, that my ResourceService (call it Controller or whatever) implemented an interface and the JAX-RS Annotations were defined on the interface, instead of the concrete class. This was not working correctly. No I define all the annotations on the concrete class and everything went fine.
Another issue, I gathered on Tomcat, but not on Jetty, was a problem with the @Path-Annotation. I defined a @Path(“/folder”) on the class, and on the concrete method I defined another @Path(“/{id}”). This throw an exception on starting up Tomcat. After removing the “/” from the second @Path (so: @Path(“{id}”). This was another step into the right direction.

KategorienJava, Projects Tags:

Java 7 – minor classloading difficulties

13. August 2011 Keine Kommentare

Since I am using Arch Linux, I am more then accustomed to using the latest and greates versions of all the stuff. Unfortunately this is not always very good. During the last couple of days I experienced a couple of class loading issues with Java 7 (as opposed to Java 6).
I am currently testing Broadleaf Commerce and had to report an issue to theses guys because of some problems I did receive during compilation and running this application. Something similar happened to me on my project at work as well. I call this “class loading issues”, but it is probably slightly more. I do have problems loading configuration data correctly. (see issue 96 on the Broadleaf JIRA)-
To work around this issue, I just installed Java 6 again. Now it is working like a charm.

KategorienArch Linux, Common, Java, Linux Tags:

JAXB Experiences II

13. August 2011 Keine Kommentare

Like already stated, we do have a large object tree, which we are exporting to xml using JAXB. Now we are also importing this tree again. Because of several Bi-/Uni-Directional Depedencies this object tree is kind of hard to reflect in XML. We do have one Root-Element, and several objects depending on this element uni-directional. Since the export is started fro the Root element, we are calling the corresponding service to find all uni-directional dependencies. These dependencies are exported into separate XML-files.
Since we do have in the child a reference to the root, we need to reflect this via an Reference on the property, otherwise the XML-file would be quite large and reflect the whole objects more then once, which leads to problems as well.
Problem is now, that the XMLIDRef is not really working, because the XMLIDRef is not referencing another object in the same file (the root element is defined in the original file). To work around this, we are using @XmlJavaTypeAdapter on the root property in the child object. This Adapter needs to get initialized and assigned to the unmarshaller. Please take a look onto the example below:

	private Object unmarshall(Class clazz, Map<Class, XmlAdapter> adapters, String fileName) throws Exception {
        // Create a JAXB context passing in the class of the object we want to marshal/unmarshal
        final JAXBContext context = JAXBContext.newInstance(clazz);

        // Create the marshaller, this is the nifty little thing that will actually transform the object into XML
		final Unmarshaller unmarshaller = context.createUnmarshaller();
		unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
		for (Class adapterClass : adapters.keySet()) {
			unmarshaller.setAdapter(adapterClass, adapters.get(adapterClass));
		}

		log.info("Starting unmarshaller");
		Object unmarshalledObject = unmarshaller.unmarshal(new FileInputStream(fileName));
		log.info("Finished unmarshaller");

        return unmarshalledObject;
	}

The concrete adapters are added in the calling method:


	public Child unmarshallEntityWrapper(RootElement element, String fileName) throws Exception {
		Map<Class, XmlAdapter> adapters = new HashMap<Class, XmlAdapter>();

		determineAdapter(element, adapters);
        return (Child)this.unmarshall(Child.class, adapters, fileName);
	}

	private void determineAdapter(RootElement element, Map<Class, XmlAdapter> adapters) {
		RootElementAdapter adapter = new RootElementAdapter();
		for (SubElement subElement : element.getSubElements()) {
			adpater.put(subElement.getId(), subElement);
		}

		adapters.put(Adapter.class, adapter);
	}

The Adapter itself is pretty straight forward:

public class RootElementAdapter extends XmlAdapter<String, RootElement> {

	private Map<String, RootElement> rootElements = new HashMap<String, RootElement>();

	public Map<String, RootElement> getRootElements() {
		return rootElements;
	}

	@Override
	public RootElement unmarshal(String id) throws Exception {
		return rootElements.get(id);
	}

	@Override
	public String marshal(RootElement rootElement) throws Exception {
		return rootElement.getId();
	}
}

The classes itself are pretty straight forward as well. Please notice, that the ChildElement is stored in another XML-file and because of this, we do need the Adapter, like already stated above. The RootElement does not have any relationship to the Child-elements:

@XmlRootElement(name = "RootElement")
public class RootElement implements Serializable {
   ...
}

public class ChildElement implements Serializable {
   @XmlJavaTypeAdapter(RootElementAdapter.class)
   public RootElement getRootElement() {
     ....
   }
}

In the service, which is handling the marshalling, we are now fetching all ChildElements belonging to the RootElement via a service method like so:

public List<ChildElement> findChildsByRootElement(RootElement rootElement) {
   ...
}

Because we would like to marshall these elements into their own XML-file, we do have to create a Wrapper Object, which basically wraps all elements:

@XmlRootElement
@XmlSeeAlso({ChildElement.class, ChildA.class, ChildB.class})
public class ChildElementWrapper {

	private Collection<ChildElement> childElements;

	public ChildElementWrapper() {
	}

	public ChildElementWrapper(Collection<ChildElement> childElements) {
		this.childElements = childElements;
	}

	@XmlElementWrapper(name = "childElements")
	@XmlElement(name = "childElement")
	public Collection<ChildElement> getChildElements() {
		return childElements;
	}

	public void setChildElements(Collection<ChildElement> childElements) {
		this.childElements = childElements;
	}
}

Another important thing, we learned during the implementation of this Import/Export layer was that the usual Inheritance from Hibernate using Discriminators is not really nicely handled. The Wrapper-Class has to use the @XmlSeeAlso-Annotation, like stated above. Now all the concrete implementations of the ChildElement-Class are marked with the type-attribute in the XML-file. Since we are using hibernates replicate and we need the Discriminator Value, we are using the following solution:

...
			switch (childElement.getElementType()) {
				case A:
					ChildA childA = (ChildA)childElement;
					this.jcDao.store(childA);
					this.jcDao.updateDiscriminator(childA, "DiscriminatorA");
					break;
				case B:
					ChildB childB = (childB)childElement;
					this.jcDao.store(childB);
					this.jcDao.updateDiscriminator(childB, "DiscriminatorB");
					break;
				default:
					break;
			}
...

	public Object store(Object object) {
		if (this.entityManager == null) throw new IllegalStateException("Entity manager has not been injected");

		Session session = (Session)this.entityManager.getDelegate();

		session.replicate(object, ReplicationMode.OVERWRITE);

		return object;
	}

	public void updateDiscriminator(Object object, String discriminator) {
		if (this.entityManager == null) throw new IllegalStateException("Entity manager has not been injected");

		Session session = (Session)this.entityManager.getDelegate();

		String hqlUpdate = "update ChildElement set type = :discriminator where id = :id";
		int updateEntities = session.createQuery(hqlUpdate).setString("id", object.toString())
									 .setString("discriminator", discriminator)
									 .executeUpdate();
	}

I hope, that this explanation is easy enough to follow ;-)

KategorienJava Tags:

Domain-Movement

31. Juli 2011 Keine Kommentare

During the last weekend, I moved my domains (javafreedom.org and wifi-frankfurt.de) to the uberspace.de location. Because of this, it can happen, that emails where not delivered or bounced.

The movement was quite fast (something like 24 hours) for all things. The guys at uberspace.de were really really nice and helped a lot. I can only recommend to any geek out there, to move her domains to uberspace.de.

KategorienCommon Tags:

Experiences with JAXB for large data structure

29. Juli 2011 Keine Kommentare

We are going to implement a Data-Export/Import out of a Spring-Hibernate Application with JAXB. During the Development of this feature, I learned quite some lessons and would like to let you participate.

First of all, we have a entity structure using inheritance all over the place. All entities are inheriting from a common “BaseEntity”. Furthermore we are using Bi-Directional relationships with Hibernate for most of the entities as well. Therefor the data-export with JAXB grows quite large and furthermore we received a “OutOfMemoryError: Java heap space”-Exception. During Trial/Error kind of learning JAXB, we received the “Class has two properties of the same name” issue. How to solve all these issues?

First of all we tried and implemented the CycleRecoverable-Interface on our BaseEntity. Therefor the BaseEntity looks like the following:

@MappedSuperclass
public class BaseEntity implements Serializable, GenericEntity<String>, CycleRecoverable {

    @Id
    @DocumentId
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    protected String id;

    @Version
    protected Integer version;

    public Object onCycleDetected(Context context) {
	    BaseEntity entity = new BaseEntity();
	    entity.setId(this.getId());
	    return entity;
    }

    @XmlID
    public String getId() {
        return this.id;
    }

// more getter and setter

On looking closely to the above code, you will recognize the method onCycleDetected. This method is defined in the interface “com.sun.xml.bind.CycleRecoverable”.

HINT: Do not use the sometimes suggested “com.sun.xml.internal.bind.CycleRecoverable” interface, if you do not have the “correct” interface, add the following dependency to your pom:

        <dependency>
            <groupId>sun-jaxb</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2</version>
        </dependency>

Furthermore you will recognize, that the @XmlID-Annotation is defined on the method and not on the property. This is due to the fact, that this method is already defined in the interface GenericEntity and otherwise you would receive the “Class has two properties of the same name”-exception. This is one lesson learned, declare the @XmlID and @XmlIDREF annontations on the methods and not on the properties ;-)

One Level above the above mentioned BaseEntity we do have another Entity called BaseEntityParent, which defines a relationship to Child Objects, which contain some language specific settings:

public abstract class BaseEntityParent<T extends BaseEntityDetail> extends
		BaseEntity {

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
		   mappedBy = "parent", orphanRemoval = true)
	@MapKey(name = "isoLanguageCode")
	private Map<String, T> details;

   // more stuff in here

This object did cause the “Class has two properties of the same name”-exception in the first place, because the Child Object references the parent itself as well. Therefor we implemented the CycleRecoverable interface in the BaseEntity.

@MappedSuperclass
public class BaseEntityDetail<T extends BaseEntityParent> extends BaseEntity {

	@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	private T parent;

	@Size(max = 10)
	@NotNull
	@Column(nullable = false, length = 10)
	private String isoLanguageCode;

After the implementation of the CycleRecoverable-Interface this problem was gone. We still received, like already stated the Out-Of-Memory exception on an Object with a large set of dependent Objects. Therefor we are now using the @XmlIDREF annotation in the related bi-directional objects. In the following we do have the Object module, which does have some bi-directional relationships to other objects:

@Entity
@XmlRootElement(name = Module.MODEL_NAME)
@XmlSeeAlso(ModuleDetail.class)
public class Module extends BaseEntityParent<ModuleDetail> implements
		Serializable {

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "module", orphanRemoval = true)
	@OrderBy("id")
	@XmlElementWrapper(name = Document.MODEL_NAME_PLURAL)
	@XmlElement(name = Document.MODEL_NAME)
	private Set<Document> documents = new HashSet<Document>();

// more properties
@Entity
@XmlRootElement(name = Document.MODEL_NAME)
@XmlSeeAlso(DocumentDetail.class)
public class Document extends BaseEntityParent<DocumentDetail> {

	@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
	@JoinColumn
	private Module module;

	@XmlIDREF
	public Module getModule() {
		return module;
	}

The Document defines now the @XmlIDREF on the method, like stated above (otherwise we did receive the “Class has two properties of the same name”-exception). This makes sure, that the module is just marshalled once and all references to this object are now just marshalled with the ID of the object.

Most probably there is still some space for improvements, but this approach (after implemented on all related objects) did save enough space to get rid of the “Out of Memory”-Exception. On implementing this “pattern”, we lowered the memory used for the marshalled XML-file quite a lot. In the first successful run, the XML-file was around 33MB, now it is just around 2MB for the same set of data.

One more word about the implementation of the CycleRecoverable interface. You can remove this implementation as soon as you have put all @XmlID and @XmlIDREF in place. For me it was really easy to find all missing parts, because I have had already a marshalled object tree in place (with CycleRecoverable) and could easily find out the missing parts (without CycleRecoverable) because of the error-messages, which are like:

MarshalException: A cycle is detected in the object graph.
This will cause infinitely deep XML:
ff80808131616f9b01316172b9840001 -> ff80808131616f9b013161797cda0019 ->
ff80808131616f9b01316172b9840001

I was able to search for the Entities using the ids in the already marshalled file ;-)

I hope you do find this one helpful. Give it a try for yourself, and report back (in the comments or via email) if you have problems.

So, basically you do not need to implement the CycleRecoverable interface, if you put @XmlID and @XmlIDREF in place, thats definitly another lesson I learned during this implementation.

I think, our entity structure is not as unsual and we do have implemented some other nice stuff (eg. the Language specific stuff with the details) which could be useful for you as well. So, if you have any question about this, I am more then willing to help you out as well.

KategorienJava, Projects Tags:

Spring WebMVC Ajax Post-Requests

21. Juni 2011 Keine Kommentare

Today I tried to improve the performance of our newest webapp. We are using a DOJO Tree for displaying the structure of a document on the left side. This tree can be quite big and the loading of this tree takes quite some time. Therefor I decided to use AJAX to load only fragments of the page if possible.

This went quite well for most of the pages, because for the GET Requests, you can easily use something like

     var xhrArgs = {
         url: url,
         content: { "fragments": "body" },
         headers: {"Accept": "text/html;type=ajax"},
         handleAs: "text",
         preventCache: false,
         sync: true,
         handle: function(response, ioargs) {
             //alert(response);
         },
         load: Spring.remoting.handleResponse,
         error: Spring.remoting.handleError
     }

     //Call the asynchronous xhrGet
     var foo = dojo.xhrGet(xhrArgs);

Unfortunately this is not as easy for Post-Requests. Therefor you can easily use (I found it after quite some googleing) this one. Please take a close look onto the end of the page and you will see an example showing exactly this one. You have to use the formId parameter, otherwise it will not work as well.

KategorienJava Tags:

Hudson vs. Jenkins – Jenkins releasing often

9. März 2011 Keine Kommentare

This seems to be one of the main differences between the two projects, the Release Cycle. While Hudson is right now at 1.396 Jenkins is already at 1.400. I wander if the bugfixes in Jenkins are all merged back into Hudson, since there are quite some “nice” fixes in there, especially some for Maven projects (see the Jenkins Changelog). Both projects have still not fixed my personal highlight bug (Error Generating Documentation). Hope that one of those will fix it soon. I promise that I will use the corresponding fork and will not look back ;-)

Lets see, which fork is going to win this race.

KategorienJava, Projects Tags:

Hudson vs. Jenkins – Hudsons future

8. März 2011 Keine Kommentare

Yesterday Sonatype announced a free webinar about the future of Hudson. I am very interested in the presentation from Jason van Zyl and the outcome therefor. So, I guess up until then, there will be no real news about this topic.

The presentation seems to be focused on Sonatypes (positive) influence on Hudson and the changes they would like to do. I guess, that this will contain also some output from the survey Sonatype has taken on the “Future of Hudson”.

Since JSR330 (Dependency Inejction) is now already implemented in Hudson, I guess that the Plugin API will change slightly, to use this new concept (which is great IMHO). Furthermore there will be (IMHO) some changes to the UI of Hudson to make a clearer distinction between Hudson and Jenkins.

So, IMHO Hudson is going to be an integral part of the offerings from Sonatype, let’s see where this leads the project and the community.

KategorienJava, Projects Tags:

Spring 3 and Hibernate Envers

4. März 2011 4 Kommentare

I wanted to add Audit functionalities to an application I am writing currently. I know that this seems to be possible with Spring Data JPA, but since this project has just reached Version 1.0.0.M1 I wanted to wait to use this one. Furthermore the application I am working on is already based on hibernate and some GenericDAO stuff we did with Hibernate, therefor a move does not seem to be too easy. Therefor I wanted to use Hibernate Envers.

The setup seems to be quite easy, following the steps in the documentation (Envers Documentation). I provided the @Audited Annotation to all Entities to be audited and provided also a new RevEntity and a new RevListener:

import javax.persistence.Entity;

import org.hibernate.envers.DefaultRevisionEntity;
import org.hibernate.envers.RevisionEntity;

@Entity
@RevisionEntity(RevListener.class)
public class RevEntity extends DefaultRevisionEntity {

    private String userName;

    public String getUserName() {
 	return userName;
    }

    public void setUserName(String userName) {
 	this.userName = userName;
    }
}
import org.hibernate.envers.RevisionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class RevListener implements RevisionListener {

    @Autowired
    private Member member;

    public void newRevision(Object revisionEntity) {
	RevEntity revEntity = (RevEntity)revisionEntity;

	Member member = MemberHolder.getMember();

	String userName = null;
	if (member != null) {
		userName = member.getUsername();
	}

	revEntity.setUserName(userName);
    }
}

The point here is to show, that I am using the Member, to find out what the current user is in our application.

Now, how do I use the newly created listener in Spring? After using Google a little, I found this post, which uses Envers, but unfortunately with the SessionFactory instead of the JPA EntityManager, like we are using. Furthermore I found this, which did not really help me as well, since there was another error message coming up, that my RevListener could not get instantiated. Since the custom EventListener was using Spring Dependency Injections for the Member, I could not use the above mentioned solution. I had to find a way to use the Spring Beans. See this blog post, which describes the problem and a possible solution.

So, basically one step back and the whole stuff again, this time using a Holder for our Members, which provides the current Member (User) of the system. This is done using the current security-context of the application and determine the member therein (see StackOverflow).

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

/**
 * @link http://stackoverflow.com/questions/360520/unit-testing-with-spring-security
 */
public class MemberHolder {

    private MemberHolder() {
        // hidden default constructor, this is a "normal" utility class
    }

    public static Member getMember() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        Object principal = auth.getPrincipal();
        Member member;

        if (principal instanceof Member) {
            member = (Member) principal;
        } else {
            return null;
        }

        if (member.getId() == null) {
            return null;
        }
        return member;
    }
}

This member-holder is then called in the RevisionListener:

import org.hibernate.envers.RevisionListener;

import de.xxx.RevEntity;
import de.xxx.model.MemberHolder;

public class RevListener implements RevisionListener {

    public void newRevision(Object revisionEntity) {
	RevEntity revEntity = (RevEntity)revisionEntity;

	String userName = MemberHolder.getMember().getUsername();

	revEntity.setUserName(userName);
    }
}

Furthermore the application-context.xml was now corrected to look like this:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
		  depends-on="transactionManager">
	<property name="persistenceUnitName" value="persistenceUnit"/>
	<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
	<property name="jpaVendorAdapter">
		<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
	</property>
	<property name="jpaDialect">
		<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
	</property>
	<property name="jpaProperties">
		<props>
			<prop key="hibernate.dialect">${hibernate.dialect}</prop>
			<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
			<prop key="org.hibernate.envers.auditTablePrefix">AUD_</prop>
			<prop key="org.hibernate.envers.auditTableSuffix"></prop>
			<prop key="org.hibernate.envers.storeDataAtDelete">true</prop>
			<prop key="hibernate.ejb.event.post-insert">
	org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener
			</prop>
			<prop key="hibernate.ejb.event.post-update">
	org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener
			</prop>
			<prop key="hibernate.ejb.event.post-delete">
	org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener
			</prop>
		        <prop key="hibernate.ejb.event.pre-collection-update">
				org.hibernate.envers.event.AuditEventListener
			</prop>
			<prop key="hibernate.ejb.event.pre-collection-remove">
				org.hibernate.envers.event.AuditEventListener
			</prop>
			<prop key="hibernate.ejb.event.post-collection-recreate">
				org.hibernate.envers.event.AuditEventListener
			</prop>
		</props>
	</property>
</bean>

Some pitfalls I stumbled upon. Do not make the property look nice, e.g.:

<prop key="hibernate.ejb.event.post-update">
    org.hibernate.ejb.event.EJB3PostUpdateEventListener,
    org.hibernate.envers.event.AuditEventListener
</prop>

Your application Context will look nice, but you will get a ClassNotFoundException ;-(

Furthermore, your Custom Event Listener should not appear in the Events, e.g. do not do:

<prop key="hibernate.ejb.event.post-update">
    org.hibernate.ejb.event.EJB3PostUpdateEventListener,de.xxx.RevListener
</prop>

This will lead to an exception like

Caused by: org.hibernate.MappingException: Unable to instantiate specified event (post-update) listener class: de.xxx.RevListener
	at org.hibernate.cfg.Configuration.setListeners(Configuration.java:1766)
	at org.hibernate.ejb.Ejb3Configuration.setListeners(Ejb3Configuration.java:1550)
	at org.hibernate.ejb.EventListenerConfigurator.setProperties(EventListenerConfigurator.java:183)
	at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:1085)
	at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:685)
	at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:73)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
	... 53 more
Caused by: java.lang.ArrayStoreException: de.xxx.RevListener
	at org.hibernate.cfg.Configuration.setListeners(Configuration.java:1763)

So, I do hope, that this helps you. It did help me ;-)

KategorienCommon, Java, Projects Tags: