Simple example of Hibernate Envers

Hibernate envers offer an easy way to track changes to entities managed by hibernate.  This post will show you how to create a very simple hibernate project with one entity and track changes made to that entity.

The project is created using maven, java 8 and hibernate 4.3.6-Final.  The hibernate version is important, if you are using a much older version of hibernate you will need extra configuration in the persistence.xml.

All the code can be found in https://github.com/jwatters1981/blog_code.git

We start with the maven pom.  In the pom we have declared the relevant dependencies needed including hibernate, junit and postgres.  You can use a different database if you wish.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>HibernateEnvers</groupId>
    <artifactId>HibernateEnvers</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.6.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-envers</artifactId>
            <version>4.3.6.Final</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <version>1.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.3-1100-jdbc41</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>

    </build>
</project>

Next is our last piece of xml configuration, it is the persistence.xml. This should be located in the META-INF
folder for hibernate to find it.

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">
    <persistence-unit name="manager1" transaction-type="RESOURCE_LOCAL">
        <class>org.acme.hibernate.envers.Project</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.user" value="postgres" />
            <property name="javax.persistence.jdbc.password" value="liverpool" />
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/envers" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />

            <property name="hibernate.hbm2ddl.auto" value="create-drop" />

        </properties>
    </persistence-unit>
</persistence>

In the above xml we reference our Project entity in line 6. We also configure out database settings.  The last property is used to create and drop our tables when the application starts.

We can now start to look at the java code, below is the Project entity, this is the entity we will be creating and modifying.  Note the extra annotation @Audited.  This tells hibernate to audit changes made to this entity.  If you don’t specify a table name for the @Audited annotation it will suffix the current table name with _AUD.

package org.acme.hibernate.envers;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.envers.Audited;

@Entity
@Table
@Audited
public class Project implements Serializable {


    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "PROJECT_NAME")
    private String name;

    @Column(name = "DESCRIPTION")
    private String description;

    @Column(name = "OWNER_ID")
    private String ownerId;
    
    public Project()
    {
        super();
    }

    public Project(String name, String description, String ownerId) {
        super();
        this.name = name;
        this.description = description;
        this.ownerId = ownerId;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getOwnerId() {
        return ownerId;
    }

    public void setOwnerId(String ownerId) {
        this.ownerId = ownerId;
    }
    
    
}

We are now ready to create a test class to save and update our entity.

package org.acme.hibernate.envers;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import org.junit.Assert;
import org.junit.Test;

public class PersonTest {

    @Test
    public void testVersioning() {
        EntityManagerFactory emf = Persistence
                .createEntityManagerFactory("manager1");
        EntityManager entityManager = emf.createEntityManager();
        entityManager.getTransaction().begin();

        Project project = new Project("Project 1", "Hibernate", "Joe Blogs");

        entityManager.persist(project);

        entityManager.getTransaction().commit();
        entityManager.getTransaction().begin();
        Project project2 = entityManager.find(Project.class, project.getId());
        project2.setOwnerId("New Owner ID");
        entityManager.merge(project2);
        entityManager.getTransaction().commit();
        entityManager.getTransaction().begin();
        project2 = entityManager.find(Project.class, project.getId());
        project2.setOwnerId("New Owner ID 2");
        entityManager.merge(project2);
        entityManager.getTransaction().commit();
        
        entityManager.getTransaction().begin();
        project2 = entityManager.find(Project.class, project.getId());
        project2.setOwnerId("New Owner ID 3");
        entityManager.merge(project2);
        entityManager.getTransaction().commit();
        AuditReader reader = AuditReaderFactory.get(entityManager);
        Assert.assertEquals(4, reader.getRevisions(Project.class,project.getId()).size());
    }
}

To build and run the above project just execute the below command.

mvn clean build

Thats it if you look at the PROJECT_AUD table you can see the records created by hibernate envers

Spring Transaction Gotcha

Previously a follow developer asked me to have a look at a strange transactional problem he was having with Spring.  He had a class like below.

 

@Service
public class TransactionProblem {
    public void entryPointToClass()
    {
        doTransactionalProcessing();
    }

    @Transactional
    private void doTransactionalProcessing() {
        //do some db stuff
        
    }
}

He was wondering why the doTransactionalProcessing was not being executed in a transaction.  The reason is depending on the spring configuration.  Spring can use CGLIB to subclass you service class and delegate all calls to your service class.  It works something like below

public class TransactionProblemProxy extends TransactionProblem {

    @Override
    public void entryPointToClass() {
        //spring starts the transaction here if the method is annotated @Transactional (its not)
        super.entryPointToClass();
        //spring commits the transaction here if the method is annotated @Transactional (its not)</pre>
    }

    
}

So as you can see from the above code when we call our service class it actually calls the TransactionProblemProxy.  The method is not annotated transactional so no transaction is created.  The call to the private method doTransactionalProcessing is not done through spring so the annotation is ignored.The solution to the above issue is either make the doTransactionalProcessing public and call it seperately or change the spring configuration to use AspectJ.

Spring-WS Contract First Web Service Example

This post will show you how to write contract-first Web services.  Contract-first web services start with defining and creating the xsd and wsdl (the contract) before writing any java code.  During this post we will create a very simple application that will expose one web service.  All code for this application can be found at https://github.com/jwatters1981/spring-web-services

As stated previously when creating contract-first web services the first step is to create the contract i.e. the xsd.  Below is the xsd file we will be using.

 

Step 1 Create the XSD

<xsd:schema xmlns="http://www.springwebservices.org/product/schema/beans"
            targetNamespace="http://www.springwebservices.org/product/schema/beans"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="get-product-request">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="name" type="xsd:string" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    
    <xsd:element name="product-response">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="product" type="product"
                             minOccurs="0" maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:complexType name="product">
        <xsd:sequence>
            <xsd:element name="code" type="xsd:string" />
            <xsd:element name="price" type="xsd:double" />
            <xsd:element name="description" type="xsd:string" />
        </xsd:sequence>
    </xsd:complexType>

</xsd:schema>

 

The xsd file is pretty straight forward we have a get-product-request element which contains a string element, a product element which holds code, price and description and a product-response element which holds a list of products which will be returned from the web service method.

Step 2 Generate Java Sources
The second step is to generate java source files from the xsd, this can be done with the xjc utility which comes with java.  To run this simply open up a command prompt for windows or a terminal for mac and linux and type xjc -d generated <path to xsd> for me this was   xjc -d generated /Users/johnwatters/spring-web-services/src/main/resources/product.xsd

You can build this source code generation into your maven build process but for this post I used the command approach and copied the generated source into my project.  You would obviously not do this in a real project.

After you run the xjc command you should see output like below

parsing a schema…
compiling a schema…

org/springwebservices/product/schema/beans/GetProductRequest.java
org/springwebservices/product/schema/beans/ObjectFactory.java
org/springwebservices/product/schema/beans/Product.java
org/springwebservices/product/schema/beans/ProductResponse.java
org/springwebservices/product/schema/beans/package-info.java

An example of some of the code generated can be seen below.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = &quot;product&quot;, propOrder = {
    &quot;code&quot;,
    &quot;price&quot;,
    &quot;description&quot;
})
public class Product {

    @XmlElement(required = true)
    protected String code;
    protected double price;
    @XmlElement(required = true)
    protected String description;

Copy the generated source into a maven project.

 

Step 3 Create a Spring web service Endpoint class

Below you will see the code that makes up the web service endpoint.  In order to tell spring that this is indeed and web service endpoint you need to annotate it with @Endpoint as seen below.

@Endpoint
public class SampleEndpoint {

    public final static String NAMESPACE = &quot;http://www.springwebservices.org/product/schema/beans&quot;;
    public final static String GET_PERSONS_REQUEST = &quot;get-product-request&quot;;

    /**
     *
     * @param code
     * @return
     */
    @PayloadRoot(localPart = GET_PERSONS_REQUEST, namespace = NAMESPACE)
    public @ResponsePayload ProductResponse getProducts(@RequestPayload GetProductRequest  code) {
        ProductResponse productResponse = new ProductResponse();
        Product product = new Product();
        product.setCode(&quot;Code1&quot;);
        product.setPrice(100.00);
        product.setDescription(&quot;test&quot;);
        productResponse.getProduct().add(product);
        return productResponse;
    }

}

The getProducts method is annotated with the @PayloadRoot annotation to enable spring to call the required method for the soap message.  This endpoint class could potentially have many method consuming different soap messages.

 

 

Step 4 The Spring context Files

Below is the spring context file for the application.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.springwebservices" />

    <bean id="productService"
        class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition"
        p:portTypeName="Product" p:locationUri="/productService/"
        p:requestSuffix="-request" p:responseSuffix="-response">
        <property name="schema">
            <bean class="org.springframework.xml.xsd.SimpleXsdSchema" p:xsd="classpath:/product.xsd" />
        </property>
    </bean>

    <bean
        class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">

    </bean>

    <bean
        class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
        <description>Enables the MessageDispatchServlet to invoke methods
            requiring OXM marshalling.
        </description>
        <constructor-arg ref="marshaller" />
    </bean>

    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPath" value="org.springwebservices.product.schema.beans" />
        <property name="schema" value="classpath:/product.xsd" />
    </bean>

</beans>

The important lines are line number 10 which is the component scan.  This tells spring to look for any classes that are annotated, like our @Endpoint class.  Line number 12 to 17 expose the wsdl.  Once the application is up and running its this configuration that allows you to go to http://localhost:8080/spring-web-services/productServices.wsdl and see the wsdl.

This MarshallingMethodEndpointAdapter uses the generated source code to marshal the incoming requests.

Step 5 the web.xml

The important part of the web xml is the spring-ws.  This servlet takes in our spring context file and routes the requests onto our endpoint.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">
 
    <servlet>
        <servlet-name>spring-ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:/spring-ws-context.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-ws</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

Step  6 The Maven pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springwebservices</groupId>
    <artifactId>spring-web-services</artifactId>
    <version>1.1.4</version>
    <name>Simple Spring Web Services</name>
    <description>Spring web services using JaxB.</description>
    
    
    <properties>

        <org.springframework.version>3.2.3.RELEASE</org.springframework.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.1.1.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>

    </dependencies>

    <build>
    
        <plugins>
            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
            </plugin>

        </plugins>
        <pluginManagement>
        
            <plugins>
            
                <!--This plugin's configuration is used to store Eclipse m2e settings
                    only. It has no influence on the Maven build itself. -->
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>org.jvnet.jaxb2.maven2</groupId>
                                        <artifactId>maven-jaxb2-plugin</artifactId>
                                        <versionRange>[0.8.2,)</versionRange>
                                        <goals>
                                            <goal>generate</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore></ignore>
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <packaging>war</packaging>
</project>

To build the project run

mvn install tomcat:run

 

You should be able to browse to the wsdl at http://localhost:8080/spring-web-services/productService.wsdl

You can use Soap UI to generate a soap request from the above wsdl.  The url to send the soap request to is http://localhost:8080/spring-web-services/productService

You should get the below response back

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns3:product-response xmlns:ns3="http://www.springwebservices.org/product/schema/beans">
<product>
<code>Code1</code>
<price>100.0</price>
<description>test</description>
</product>
</ns3:product-response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

All source available at https://github.com/jwatters1981/spring-web-services

 

Please let me know if you found the post useful