Jørgen Ringen

Nothin but code 👨‍💻

github twitter linkedin email rss
JavaEE: Timestamps on entities with entitylistener
Jul 1, 2015
2 minutes read

This post shows how you can add timestamps to your entities in a non-intrusive unified way with plain JPA.


We start off with an embeddable JPA entity, TimeStamp:

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.util.Date;

@Embeddable
public class TimeStamp {

    @Column(name = "date_created")
    @Temporal(TemporalType.TIMESTAMP)
    private Date created;

    @Column(name = "date_updated")
    @Temporal(TemporalType.TIMESTAMP)
    private Date updated;

    // getters, setters, etc
}

We also need a simple interface that our entities should implement:

public interface EntityWithTimeStamp {

    void setTimeStamp(TimeStamp timeStamp);
    TimeStamp getTimeStamp();
}

Lets add an entity which implements our interface:

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

@Entity
@EntityListeners(TimeStampListener.class)
public class Person implements EntityWithTimeStamp {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @Embedded
    private TimeStamp timeStamp;

    // getters, setters, etc

    @Override
    public void setTimeStamp(TimeStamp timeStamp) {
        this.timeStamp = timeStamp;
    }

    @Override
    public TimeStamp getTimeStamp() {
        return this.timeStamp;
    }
}

As you can see, the entity needs three things to make it “timestampable”:

  • Implement our timestamp-interface
  • Declare an embedded TimeStamp field
  • Use the EntityListener annotation with a callback listener class

Our EntityListener class is just a simple java class with annotated methods declaring which events from the JPA runtime it should perform actions upon:

public class TimeStampListener {

    @PrePersist
    public void persist(Object entity) {
        if (entity instanceof EntityWithTimeStamp) {
            TimeStamp timeStamp = new TimeStamp();
            timeStamp.setCreated(new Date());
            ((EntityWithTimeStamp) entity).setTimeStamp(timeStamp);
        }
    }

    @PreUpdate
    public void updated(Object entity) {
        if (entity instanceof EntityWithTimeStamp) {
            TimeStamp existingTimeStamp = ((EntityWithTimeStamp) entity).getTimeStamp();
            existingTimeStamp.setUpdated(new Date());
        }
    }
}

The listener will be used by all our entities which should contain a TimeStamp field.

An EntityListener can easily be expanded with methods @PreRemove, @PostPersist, @PostUpdate, etc. The annotations can also be put directly on the entity which means that you don’t need the EntityListener at all.


Simple Test:

class TimestampListenerTest {

    @Test
    public void testTimeStamp() throws InterruptedException {
        // create
        tx.begin();
        Person person = new Person();
        person.setName("Name1");
        person = em.merge(person);
        tx.commit();
    
        assertThat(person.getTimeStamp().getCreated(), not(nullValue()));
        assertThat(person.getTimeStamp().getUpdated(), is(nullValue()));
    
        Thread.sleep(5);
    
        // update
        tx.begin();
        person = em.find(Person.class, person.getId());
        person.setName("Name2");
        tx.commit();
    
        // verify timestamp
        assertThat(person.getTimeStamp().getCreated(), not(nullValue()));
        assertThat(person.getTimeStamp().getUpdated(), not(nullValue()));
        assertThat(person.getTimeStamp().getUpdated(), after(person.getTimeStamp().getCreated()));
    }
}

Back to posts


comments powered by Disqus