• Menu
  • Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

JavaBeat

Java Tutorial Blog

  • Java
    • Java 7
    • Java 8
    • Java EE
    • Servlets
  • Spring Framework
    • Spring Tutorials
    • Spring 4 Tutorials
    • Spring Boot
  • JSF Tutorials
  • Most Popular
    • Binary Search Tree Traversal
    • Spring Batch Tutorial
    • AngularJS + Spring MVC
    • Spring Data JPA Tutorial
    • Packaging and Deploying Node.js
  • About Us
    • Join Us (JBC)
  • Java
    • Java 7
    • Java 8
    • Java EE
    • Servlets
  • Spring Framework
    • Spring Tutorials
    • Spring 4 Tutorials
    • Spring Boot
  • JSF Tutorials
  • Most Popular
    • Binary Search Tree Traversal
    • Spring Batch Tutorial
    • AngularJS + Spring MVC
    • Spring Data JPA Tutorial
    • Packaging and Deploying Node.js
  • About Us
    • Join Us (JBC)

Introduction to Hibernate Envers

April 21, 2015 //  by JB Edit@rs//  Leave a Comment

Hibernate Envers is a library that helps us to easily achieve audit functionality. It aims to enable easy auditing/versioning of persistent classes/entities. It provides a built in mechanism to maintain history of tables in Hibernate. Hibernate Envers has been created by Adam Warski. From Hibernate 3.5, Envers is part of core module.

Envers works in a similar fashion to Version Control Systems (like Subversion/CVS etc.) which maintains Revisions for changes. In Envers, Every Transaction is a Revision (unless the transaction didn’t modify any audited entity). Revision has the same concept as “Change Set in Version Control Systems”.

also read:

  • What Hibernate Framework?
  • Interceptors in Hibernate Framework

Using Revision Number, you can fetch various details like:

  • Revision commit time stamp
  • Query for various entities at that revision

Hibernate Envers Features

  • Auditing of all mappings defined by JPA specification or Hibernate mappings (which extend JPA)
  • Querying historical data

Hibernate Envers

(source : SlideShare)

Environment Setup

  • Add hibernate-envers jar on class path
  • Add Hibernate Annotations or Entity Manager jars on class path
  • Entities must have immutable unique identifiers (primary keys).
  • Download the source code for finding the maven dependencies required for running a simple example

How Envers Works?

  • Annotate your persistent class or some of its properties that you want to audit with @Audited.
  • If you are using Old Versions of Hibernate then you may need to specify listeners (mentioned below) in Hibernate Configuration file. If you are using latest versions of Hibernate then just add Hibernate-Envers jar file in the classpath, it automatically register listeners.
<listener class="org.hibernate.envers.event.AuditEventListener" type="post-insert"/>
<listener class="org.hibernate.envers.event.AuditEventListener" type="post-update"/>
<listener class="org.hibernate.envers.event.AuditEventListener" type="post-delete"/>
<listener class="org.hibernate.envers.event.AuditEventListener" type="pre-collection-update"/>
<listener class="org.hibernate.envers.event.AuditEventListener" type="pre-collection-remove"/>
<listener class="org.hibernate.envers.event.AuditEventListener" type="post-collection-recreate"/>
  • For each audited entity, a new table will be created “entity_table_AUD”, which will hold the history of changes made to  the entity.
  • Hibernate automatically creates Audit tables if below property is set to “create, create-drop or update” in hibernate configuration file.
<property name="hbm2ddl.auto">update</property>
  • Alternatively you can also create DDL statements using Ant Tasks or manually.
  • You can then retrieve and query historical data without much effort. The audit (history) of an entity can be accessed using the AuditReader interface, which can be obtained having an open EntityManager or Session via the AuditReaderFactory.

Hibernate Envers Example

hibernate.cfg.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>

		<!-- DB Connection Details -->
		<property name="connection.url">jdbc:mysql://localhost:3306/testdb</property>
		<property name="connection.username">root</property>
		<property name="connection.password">root</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

		<!-- Logger Details : to show queries on console-->
		<property name="show_sql">true</property>
		<property name="format_sql">true</property>

		<!-- Envers automatically creates audit tables if below property is set to create, create-drop or update -->
		<property name="hbm2ddl.auto">update</property>

		<property name="connection.pool_size">1</property>
		<property name="current_session_context_class">thread</property>

		<!-- Entity mapping -->
		<mapping class="com.domain.Article" />

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

Above hibernate.cfg.xml file is simple enough to understand, below is one important property from above xml:

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

Above property is required for automatic “Audit Table Creation”, alternatively you can also create tables manually (follow the Audit Table format).

Hibernate Envers Example

I have created a simple example that illustrates how hibernate envers stores the revision data in different scenarios. This example has the following components. At the end of this tutorial, you can find a download link for downloading the source code for this example.

  1. HibernateUtil.java – Creating session factory
  2. Example 1 – Using @Audited at class level
  3. Example 2 – Using @Audited at class level and @NotAudited for properties
  4. Example 3 – Using @Audited at property level

HibernateUtil.java

It is just a Utility class for building Hibernate Session Factory.

package com.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
	private static final SessionFactory sessionFactory = buildSessionFactory();
	private static SessionFactory buildSessionFactory() {
		try {
			return new Configuration().configure().buildSessionFactory();
		} catch (Exception ex) {
			System.err.println("Build SessionFactory Failed:" + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}
	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	public static void shutdown() {
		getSessionFactory().close();
	}
}

Example 1

package com.domain;

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(name = "article")
@Audited
public class Article {

	@Id
	@GeneratedValue
	@Column(name = "id")
	private int id;

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

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

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

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
}

In Above Entity, you can see “@Audited” is used at class level, means all property of the class will be involved in Auditing.

Test Class:

package com.test;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import com.domain.Article;
import com.util.HibernateUtil;
public class TestArticle {
	public static void main(String[] args) {
		TestArticle testArticle = new TestArticle();
		// creating two articles
		Article firstArticle = getFirstArticle(createArticle());
		Article secondArticle = getSecondArticle(createArticle());
		// saving both articles
		testArticle.saveArticle(firstArticle);					// Transaction 1
		testArticle.saveArticle(secondArticle);					// Transaction 2
		// updating first article
		firstArticle.setContent("Test Content2");
		testArticle.saveOrUpdateArticle(firstArticle);			// Transaction 3

		// deleting second article
		testArticle.deleteArticle(secondArticle);				// Transaction 4
		AuditReader reader = AuditReaderFactory.get(HibernateUtil.getSessionFactory().openSession());
		// List all revisions of an entity
		List<Number> versions = reader.getRevisions(Article.class, new Integer(2));
		for (Number number : versions) {
		  System.out.print(number + " ");
		}

		// Retrieve an object in a previous version
		Article firstArticleFromDB = (Article) reader.find(Article.class, new Integer(1), 2);
		System.out.println("Output 1: " + firstArticleFromDB.getId() + " " + firstArticleFromDB.getSubject() + " " +firstArticleFromDB.getContent() + " " + firstArticleFromDB.getAuthor());

	}
	private static Article createArticle() {
		return new Article();
	}

	private static Article getFirstArticle(Article firstArticle) {
		firstArticle.setSubject("First Article : Hibernate Envers");
		firstArticle.setContent("Test Content");
		firstArticle.setAuthor("Author1");
		return firstArticle;
	}
	private static Article getSecondArticle(Article secondArticle) {
		secondArticle.setSubject("Second Article : Hibernate Envers II");
		secondArticle.setContent("Test Content");
		secondArticle.setAuthor("Author1");
		return secondArticle;
	}
	public void saveArticle(Article article) {
		Session session = HibernateUtil.getSessionFactory().openSession();
		session.beginTransaction();

		session.save(article);

		session.getTransaction().commit();
	}
	public void saveOrUpdateArticle(Article article) {
		Session session = HibernateUtil.getSessionFactory().openSession();
		session.beginTransaction();

		session.saveOrUpdate(article);

		session.getTransaction().commit();
	}
	public void deleteArticle(Article article) {
		Session session = HibernateUtil.getSessionFactory().openSession();
		session.beginTransaction();

		session.delete(article);

		session.getTransaction().commit();
	}
}

In Above Test Class, we are performing 4 transactions:

  •  Transaction 1 : Saving “firstArticle”
  • Transaction 2 : Saving “secondArticle”
  • Transaction 3 : Updating “firstArticle”
  • Transaction 4 : Deleting “secondArticle”

When we run above Test Program, two tables were created (apart from ‘article’ table, which is domain table):

  •  article_aud : This will contain history (modification/creation/deletion) of our entities. The primary key is composed of ID and REV fields.

you can see 4 revisions in article_aud table:

output_1

Since we added @Audited annotation at class level, all property of the class are available in Audit Table.

Apart from that We are having two extra columns in Audit Table:

  1. REV: Revision Number, which maintains same sequence order as you performed Transactions.
  2. REVTYPE: It defines type of Transaction whether it is Save/Update/Delete
  • 0 means creation
  • 1 means modification
  • 2 means deletion

revinfo : Contains revision time stamp

output_2

Time stamp is in “Long” format. You can easily convert it to Human readable format (Sample Code is below):

long val = 1429237881803l;
Date date=new Date(val);
SimpleDateFormat df2 = new SimpleDateFormat("dd/MM/yy");
String dateText = df2.format(date);
System.out.println(dateText);

Test program also contains some code for:

  • Listing all revisions of an entity
  • Also retrieving an object at specific revision

We are using ” AuditReader” API for querying details from Audit Tables. 

 Example 2 :

Now I am changing my domain class a little, still @Audited annotation is used at class level, but I have added @NotAudited for property “Author”.

package com.domain;
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;
import org.hibernate.envers.NotAudited;
@Entity
@Table(name = "article")
@Audited
public class Article {
 @Id
 @GeneratedValue
 @Column(name = "id")
 private int id;
 @Column(name = "subject")
 private String subject;
 @Column(name = "content")
 private String content;
 @Column(name = "author")
 @NotAudited
 private String author;

 public int getId() {
 return id;
 }
 public void setId(int id) {
 this.id = id;
 }
 public String getSubject() {
 return subject;
 }
 public void setSubject(String subject) {
 this.subject = subject;
 }
 public String getContent() {
 return content;
 }
 public void setContent(String content) {
 this.content = content;
 }
 public String getAuthor() {
 return author;
 }
 public void setAuthor(String author) {
 this.author = author;
 }
}

When we run Test program & check “article_aud” table, we can see below output:

Output_3

You can notice there is no “author” column on Audit table now.

Example 3:

Now I am changing my domain class again and instead using “@NotAudited” annotation, I am using “@Audited” annotation at property level (I removed @Audited annotation from class level).

package com.domain;

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(name = "article")
public class Article {

 @Id
 @GeneratedValue
 @Column(name = "id")
 private int id;

 @Column(name = "subject")
 @Audited
 private String subject;

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

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

 public int getId() {
 return id;
 }
 public void setId(int id) {
 this.id = id;
 }
 public String getSubject() {
 return subject;
 }
 public void setSubject(String subject) {
 this.subject = subject;
 }
 public String getContent() {
 return content;
 }
 public void setContent(String content) {
 this.content = content;
 }
 public String getAuthor() {
 return author;
 }
 public void setAuthor(String author) {
 this.author = author;
 }
}

When you run the Test program, you can see below output on Audit table:

Output_4

You can see, Audit table contains only “id” and “subject” from domain class.

Download SourceCode

[wpdm_file id=118 ]

Category: HibernateTag: Hibernate Envers

Previous Post: « AngularJS – Difference between ng-model and ng-bind
Next Post: Hibernate / JPA – Detached entity passed to persist exception »

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

Follow Us

  • Facebook
  • Pinterest

FEATURED TUTORIALS

New Features in Spring Boot 1.4

Difference Between @RequestParam and @PathVariable in Spring MVC

What is new in Java 6.0 Collections API?

The Java 6.0 Compiler API

Introductiion to Jakarta Struts

What’s new in Struts 2.0? – Struts 2.0 Framework

JavaBeat

Copyright © by JavaBeat · All rights reserved
Privacy Policy | Contact