Maintaining the history of an object

In the previous recipe, we used the versioning feature of hibernate to check how many times a particular record has been modified. This is a good feature; however, it gives us just a number. Versioning does not store the modified data anywhere. So, it's hard to find out the previous state of the object before the modification.

As a solution to this, hibernate provides another project called Envers.

Envers helps us maintain the history of the database and it keeps track of the modifications in the database table rows. For this to work, we have to change the configuration in the POJO and configuration (.cfg.xml) files.

To configure Envers with hibernate, we need JAR files in our project. You can use the following Maven dependency for the Maven-based project:

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-envers</artifactId>
  <version>4.3.10.Final</version>
</dependency>

Once we configure Envers in our application, it creates a version table that contains the fields of the original table. Whenever the original table gets modified, hibernate automatically adds an entry in the version table; so, for every insert, update, and delete operation, hibernate inserts the records in the version table. Another table is automatically created by hibernate with the name revinfo that stores revision information such as the revision id and revision timestamp.

Getting ready

Here, we will download the required libraries using the Maven dependency. The following code shows how to create the required classes and tables.

Creating the classes

The following code shows an Employee POJO and the changes in the configuration file (*.cgf.xml):

Source file: Employee.java

@Entity
@Table(name = "employee")
/* Line 3 */ @Audited
public class Employee {

  @Id
  @GeneratedValue
  private long id;

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

/* Line 13 */  @NotAudited
  @Column(name="password")
  private String password;
  
  // getters ans setters
  
}

Source file: hibernate.cfg.xml

Add the below lines to your configuration file:

<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"/>

Creating the tables

Use the following table script if the hibernate.hbm2ddl.auto configuration property is not set to create:

Use the following script to create the employee table:

CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

Use the following script to create the employee_aud table:

CREATE TABLE `employee_aud` (
  `id` bigint(20) NOT NULL,
  `REV` int(11) NOT NULL,
  `REVTYPE` tinyint(4) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`,`REV`),
  KEY `FK_REVISION_ID` (`REV`),
  CONSTRAINT `FK_REVISION_ID` FOREIGN KEY (`REV`) REFERENCES `revinfo` (`REV`)
);

Use the following script to create the revinfo table:

CREATE TABLE `revinfo` (
  `REV` int(11) NOT NULL AUTO_INCREMENT,
  `REVTSTMP` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`REV`)
);

How to do it…

Now, we will insert a record in the employee table and take a look at the tables created by hibernate with the data. Update the following code:

Code

Session session = sessionFactory.openSession();
Transaction transaction = session.getTransaction();
transaction.begin();

Employee employee = new Employee();
employee.setName("Aarush");
employee.setPassword("p@$sw0rd");
session.save(employee);

transaction.commit();
session.close();

Output

/* Line 1 */ Hibernate: insert into employee (name, password) values (?, ?)
/* Line 2 */ Hibernate: insert into REVINFO (REVTSTMP) values (?)
/* Line 3 */ Hibernate: insert into employee_AUD (REVTYPE, name, id, REV) values (?, ?, ?, ?)

The following employee table shows the data after the insertion is completed:

id

name

password

1

Aarush

p@$sw0rd

The following is the database table structure for the REVINFO table:

REV

REVTSTMP

1

1421832556098

The following is the database table structure for the employee_AUD table:

id

REV

REVTYPE

name

1

1

0

Aarush

How it works…

Now, we will discuss how this feature works. We will take a look at the changes in each file in detail.

Let's consider the Employee.java file. In the Employee class, we added the @Audited annotation at the class level shown in Line 3.

@Audited is present at the class level. This means that hibernate will enable the history of the Employee object and store the changes in the revision table.

Another useful annotation used in this class is @NotAudited, which is shown in Line 13.

Using the @Audited annotation at the class level means that all the fields of that class are involved in the auditing process. If we do not want any field to be involved in the auditing process, the @NotAudited annotation is used. For instance, here we annotate a password field with the @NotAudited annotation, so hibernate will ignore this field during auditing.

Now, let's consider the hibernate.cfg.xml file. In this configuration file, we added three new listener tags, where the class attribute defines the Listener class and the type attribute defines a type of operation, such as post-insert, post-update, and post-delete.

There are many events available in hibernate. Here, post-insert means that the auditing is done after the insertion is completed. This works in a similar way for post-update and post-delete.

There's more…

Once we execute the code, hibernate will create three tables:

  • employee: This represents the Employee class.
  • employee_AUD: This represents the audit table for the Employee class. Hibernate will create an audit table by the concatenation of the actual table name as a prefix and the "_AUD" value as a suffix if value is not provided.
  • revinfo: This stores the revision information, such as the revision id and revision timestamp.

We can change the suffix and prefix value of the table as well as the audit table name in the following way:

@AuditTable(value="emp_history")
public class Employee {
  // other fields and setters/getters
}

Now, hibernate will create the table name, emp_history; the prefix and suffix are ignored in this case.

Changing the suffix by changing the property in the configuration file

To change the audit table suffix, you can update the following configurations:

<property name="org.hibernate.envers.auditTableSuffix">
  _history
</property>

You can also use:

<property name="org.hibernate.envers.audit_table_suffix">
  _history
</property>

Note

Hibernate uses _history as the suffix if not provided. The _AUD" suffix is used by default.

Changing the prefix by changing the property in the configuration file

To change the audit table prefix, you can update the following configurations:

<property name="org.hibernate.envers.auditTablePrefix">
  history_
</property>

You can also use:

<property name="org.hibernate.envers.audit_table_prefix">
  history_
</property>

Note

Hibernate uses history_ as the prefix if not provided. The "" prefix is used by default.

Now, hibernate will create an audit table with the given configuration, which will contain all auditable fields and the REV and REVTYPE column.

The REV and REVTYPE columns are used to maintain the revisions. To change the name of the REV and REVTYPE columns, use the following code:

Renaming the REV column

To change the revision field name, you can update the following configurations:

<property name="org.hibernate.envers.revision_field_name">
  REV_COL
</property>

You can also use:

<property name="org.hibernate.envers.revisionFieldName">
  REV_COL
</property>

Note

Hibernate uses REV_COL as the column name; if not provided, REV is used by default.

Renaming the REVTYPE column

To change the revision type field name, you can update the following configurations:

<property name="org.hibernate.envers.revision_type_field_name">
  REVTYPE_COL
</property>

You can also use:

<property name="org.hibernate.envers.revisionTypeFieldName">
  REVTYPE_COL
</property>

Note

Hibernate uses REVTYPE_COL as the column name; if not provided, REVTYPE is used by default.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.36.38