Envers Quick Start

To start, you need Hibernate and Hibernate Entity Manager (see hibernate.org), with all of their dependencies, and the Envers jar (see downloads).

When configuring your persistence unit (the persistence.xml file), add the following event listeners: (this will allow Envers to check if any versioned entities were modified)

   <persistence-unit ...>
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>...</class>
        <properties>
            <property name="hibernate.dialect" ... />
            <!-- other hibernate properties -->
 
            <property name="hibernate.ejb.event.post-insert" 
	              value="org.jboss.envers.event.VersionsEventListener" />
            <property name="hibernate.ejb.event.post-update" 
	              value="org.jboss.envers.event.VersionsEventListener" />
            <property name="hibernate.ejb.event.post-delete" 
	              value="org.jboss.envers.event.VersionsEventListener" />
        </properties>
    </persistence-unit>

Then, annotate your persistent class with @Versioned - this will make all properties versioned. For example (the two entities below are taken from the console demo):

import org.jboss.versions.Versioned;

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

@Entity
@Versioned // that's the important part :)
public class Person {
    @Id
    @GeneratedValue
    private int id;

    private String name;

    private String surname;

    @ManyToOne
    private Address address;

    // add getters, setters, constructors, equals and hashCode here
}
And the referenced entity:
@Entity
@Versioned
public class Address {
    @Id
    @GeneratedValue
    private int id;

    private String streetName;

    private Integer houseNumber;

    private Integer flatNumber;

    @OneToMany(mappedBy = "address")
    private Set<Person> persons;

    // add getters, setters, constructors, equals and hashCode here
}

And that's it! You create, modify and delete the entites as always. If you look at the generated schema, you will notice that it is unchanged by adding versioning for the Address and Person entities. Also, the data they hold is the same. There are, however, two new tables - Address_versions and Person_versions, which store the historical data, whenever you commit a transaction.

Instead of annotating the whole class and versioning all properties, you can annotate only some persistent properties with @Versioned. This will cause only these properties to be versioned.

You can access the versions of an entity using the VersionsReader interface, which you can obtain when having an open EntityManager: (See also the javadocs.)

VersionsReader reader = VersionsReaderFactory.get(entityManager);
Person oldPerson = reader.find(Person.class, personId, revision)

The T find(Class cls, Object primaryKey, Number revision) method returns an entity with the given primary key, with the data it contained at the given revision. If the entity didn't exist at this revision, null is returned. Of course, only the versioned properties will be set on the returned entity. The rest will be null.

You can also get a list of revisions at which an entity was modified using the getRevisions method, as well as retrieve the date, at which a revision was created using the getRevisionDate method.

A short example

For example, using the entities defined above, the following code will generate revision number 1, which will contain two new Person and two new Address entities:

entityManager.getTransaction().begin();

Address address1 = new Address("Privet Drive", 4);
Person person1 = new Person("Harry", "Potter", address1);

Address address2 = new Address("Grimmauld Place", 12);
Person person2 = new Person("Hermione", "Granger", address2);

entityManager.persist(address1);
entityManager.persist(address2);
entityManager.persist(person1);
entityManager.persist(person2);

entityManager.getTransaction().commit();

Now we change some entities. This will generate revision number 2, which will contain modifications of one person entity and two address entities (as the collection of persons living at address2 and address1 changes):

entityManager.getTransaction().begin();

Address address1 = entityManager.find(Address.class, address1.getId());
Person person2 = entityManager.find(Person.class, person2.getId());

// Changing the address's house number
address1.setHouseNumber(5)

// And moving Hermione to Harry
person2.setAddress(address1);

entityManager.getTransaction().commit();

We can retrieve the old versions easily:

VersionsReader reader = VersionsReaderFactory.get(entityManager);

Person person2_rev1 = reader.find(Person.class, person2.getId(), 1);
assert person2_rev1.getAddress().equals(new Address("Grimmauld Place", 12));

Address address1_rev1 = reader.find(Address.class, address1.getId(), 1);
assert address1_rev1.getPersons().getSize() == 1;

// and so on