I try to log any changes of my JPA entities. For this reason each entity inherits from an abstract entity class which has a list of LogEntry objects.
AbstractEntity class:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@EntityListeners(ChangeListener.class)
public abstract class AbstractEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private Long version;
@Temporal(TemporalType.DATE)
private Date validFrom;
@Temporal(TemporalType.DATE)
private Date validTo;
private String name;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "abstractEntity")
private List<LogEntry> logEntry = new ArrayList<LogEntry>();
//getter and setter
}
LogEntry class:
@Entity
public class LogEntry extends AbstractEntity {
@ManyToOne
@JoinColumn
protected AbstractEntity abstractEntity;
@ManyToOne
@JoinColumn
protected Person person; // creator or updater
@Column(updatable=false, insertable=false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
@Temporal(TemporalType.TIMESTAMP)
protected Date changeDate;
protected String comment;
//getter and setter
}
My approach for this was to create a new LogEntry object and add it to the entity's LogEntry list before an entity got updated or persisted.
I tried the following solutions:
Nearly each of these trials enables me to change attributes (like name, or validTo) of the affected entity. But no solution provides the opportunity to create a new LogEntry instance, or rather to persist this LogEntry instance. I also tried to get an instance of a session bean via jndi lookup to persists the LogEntry manually. The jndi lookup works, but calling create or update methods of the session bean has no effect.
My current entity listener looks like this:
public class ChangeListener extends DescriptorEventAdapter {
@Override
public void preUpdate(DescriptorEvent event) {
AbstractEntity entity = (AbstractEntity) event.getObject();
if (!(entity instanceof LogEntry)) {
LogEntry logEntry = new LogEntry();
logEntry.setPerson(getSessionController().getCurrentUser());
logEntry.setAbstractEntity(entity);
entity.getLogEntries().add(logEntry);
}
}
}
Hibernate Envers is for various reasons no option.
EclipseLink Version is 2.3.2.
Persisting new object during events from the commit process can be difficult.
You could obtain the changes before the commit and persist you logs (get change set from the UnitOfWork).
See, http://wiki.eclipse.org/EclipseLink/FAQ/JPA#How_to_access_what_changed_in_an_object_or_transaction.3F
Otherwise, it is possible to directly insert and object from inside an event.
i.e.
event.getSession().insertObject(logEntry);
Temporarily I solved this issue by comparing the current entity and the old entity in preUpdate. The comparison is done with EqualsBuilder from Apache Commons Lang.
public class ChangeAbstractEntityListener extends DescriptorEventAdapter {
@Override
public void preUpdate(DescriptorEvent event) {
AbstractEntity originalEntity = (AbstractEntity) event.getOriginalObject();
AbstractEntity entity = (AbstractEntity) event.getObject();
if (!EqualsBuilder.reflectionEquals(originalEntity, entity, true)) {
if (!(entity instanceof LogEntry)) {
LogEntry logEntry = new LogEntry();
logEntry.setPerson(getSessionController().getCurrentUser());
logEntry.setAbstractEntity(entity);
entity.getLogEntries().add(logEntry);
}
}
}
//jndi lookup to get the user object
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With