Bi-temporal versioning with Envers

With the recent addition of queries to Envers, it is now possible to easily retrieve data in a bi-temporal way. If you are not familiar with bi-temporal versioning, a good introduction by Martin Fowler can be found here.

Suppose we want to store information about Persons and Addresses, at which these persons live. Moreover, we also want to store dates: when a Person registered the fact that he/she moved, and when did he/she actually move – these may be different, as a person may have first moved, and only registered the fact after a week. Storing the two dates: registered and actual, for some entity, is the basis of bi-temporal versioning.

We can store this information in a Registration entity, which is in a one-to-one relationship with Person, and one-to-many with Address. (Each Person can have at most one Registration, and therefore also Address; each Address can have many Registrations, and therefore many Persons living there.) The Registration entity can also store the registered and actual dates. Additionally, it can be versioned, so that all historical information is remembered.

Using queries, we can, for example, answer this question: “What did we know about the address of person X in the state of the database from date Y?”. To do this, we have to select a registration, which has the actual date as big (recent) as possible, but both the registration and actual dates must be before Y. Hence, the query can look like this:

.forRevisionsOfEntity(Registration.class, true)
    .add(VersionsRestrictions.relatedIdEq("person", personXId))
    .add(VersionsRestrictions.le("registrationDate", dateY))
    .add(VersionsRestrictions.le("actualDate", dateY)))

You may experiment with bi-temporal versioning using the updated Envers+Seam demo. To run the demo, you’ll need JBoss AS 4.2. After downloading, unzip and deploy the -ds.xml and .ear files, start the server, and go to http://localhost:8080/envers_seam_demo. A screenshot from the demo:

Moreover, the second beta release of Envers is available. It contains minor feature additions (release notes).


  • Andre Fuechsel

    As far as I understand, the bi-temporal query does only work, because the relation between Person and Address itself is versioned as a separate entity Registration. Is that correct? Do you plan to enhance envers in a way to have full bi-temporal entities? IHMO it would be only necessary to store the registration data in addition to the actual date for each revision. Am I right or do I miss something here?

  • You’re right, that instead of storing actual date, you can use date of the revision, which is automatically stored. I used an explicit acutal date so that the presentation of the example is clear. (It can also be the case that the actual date is not “now”, but something else.)

    I don’t quite understand what do you mean by “full bi-temporal”? You could remove the Registration entity and merge it with Person, and the query would work the same, however I think it’s less logical (what does “actual date” mean for a Person?).

    The main constraint of the current query system is that you cannot traverse relations (that is, you cannot specify constraints on fields of related entities, only specify their IDs). However, this will be removed in the future:


  • Actually, quite frankly, the commentary is more interesting messages themselves. (Not to insult the author, of course:))