1

I need to implement Historization in the sense that we must have a new record for any updated record. All records have a deleted and inserted date. So what we do is set the deleted date to NOW when updating, and inserting a new record with insertion date NOW and deleted date NULL.

I am using JPA with eclipseLink as the persistent provider. My problem is that I need to do the "extra" insert when JPA think that it needs to update. So far I have set updatable=false on the Entity for non meta data fields. I update the insertionDate and deletionDate and the userName. Which all works as expected. All I now need to do is to make the extra insert.

I am trying to solve the issue using an entityListener, in which I implement @postUpdate and @preUpdate. I can't get it to work as I end up in a deadlock if I try to commit the new transaction that I do on @postUpdate. If I do not make a new EntityManager and a transaction, but mere call persist on the existing EntityManager nothing happens with the "new" entity for some reason.

Code below:

import java.util.Calendar;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import javax.persistence.PreUpdate;

public class ColorEntityListener {

    public ColorEntityListener()
    {       
    }

    @PreUpdate 
    void onPreUpdate(ColorEntity c) 
    {
        System.out.println("onPreUpdate");
        c.setDeletionDate(Calendar.getInstance());      
    }

    @PostUpdate 
    void onPostUpdate(ColorEntity c) 
    {   
        System.out.println("onPostUpdate");
        createNewRecord(c);
    }

    @PostPersist
    void onPostPersist(ColorEntity c) 
    {   
        System.out.println("onPostPersist");
    }

    private void createNewRecord(ColorEntity c) {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("v2_tl");
        EntityManager em = factory.createEntityManager();

        em.getTransaction().begin();

        ColorEntity cNew = new ColorEntity();
        cNew.setPrNumber(c.getPrNumber());
        cNew.setHs(c.getHs());
        cNew.setCountryCode(c.getCountryCode());
        cNew.setValidFrom(c.getValidFrom());
        cNew.setValidUntil(c.getValidUntil());
        cNew.setColorCode(c.getColorCode());

        //cNew.setId(0);
        cNew.setInsertDate(Calendar.getInstance());
        cNew.setDeletionDate(null);

        em.persist(cNew);

        System.out.println("Before commit of new record");
        em.getTransaction().commit();
        System.out.println("After commit of new record");
    }
}
Michael
  • 397
  • 2
  • 10
  • having a inserted and deleted date is redundant and might get you into trouble if for some reason there is a gap between a deleted and the next inserted date. Especially if you set the dates as in your example, because the different methods may be executed at times where `Calendar.getInstance()` returns values that are one after another. – SpaceTrucker Mar 03 '15 at 08:18
  • related: http://stackoverflow.com/questions/9542366/how-to-implement-a-temporal-table-using-jpa – SpaceTrucker Mar 03 '15 at 08:22
  • Obviously in the finished example I would set the Calendar to an object locally and use that for both statements. I am curious though, how would you indicate a deleted row without a deletionDate? – Michael Mar 03 '15 at 12:59
  • Depends on your exact usecase, but some possible options are using only an inserted date and the deleted rows are those where a more up-to-date row exists or the other way round, using only a deleted date and the newest row hasn't it set. – SpaceTrucker Mar 03 '15 at 13:47

2 Answers2

2

Eclipselink has built in support for History by adding a @Customizer. See this link for an example on how to use it:

http://wiki.eclipse.org/EclipseLink/Examples/JPA/History

stalet
  • 1,345
  • 16
  • 24
  • As far as I can see, it only solves the problem if you want to store data in a separate history table. What we want is to store the data in the same table as the entity belongs too. – Michael Mar 03 '15 at 12:00
1

Please, look at hibernate envers project. Its solves revers task - hold information about changes audit. But it stores information in good for your task format.