Last time I wrote about the automatic entity change tracking and I would like to share my new insights on the subject. The reason for this is, that some (including me) may find it somewhat disturbing to make the entities implement an interface or even extend another baseclass. Today, we gonna try the same thing using reflection.
So, just as before, we create an entity called Invoice. However, this time it does not implement or extend any other interface or abstract class. Thus, it should look as following:
@EntityAs you can see, this time we are not implementing the Traceable interface. For this reason, we need to modify the AuditLogger Entity Listener class, which uses following code to identify an entity as being traceable:
@Table(name = "invoice")
public class Invoice {
@Id
@GeneratedValue
@Column(name = "id")
private Integer id;
@Column(name = "product");
private String product;
@Column(name = "receiver");
private String receiver;
@Column(name = "amount")
private Double amount;
@Column(name = "created")
@Temporal(TemporalType.TIMESTAMP)
private Date created;
@Column(name = "created_by")
private String createdBy;
@Column(name = "changed")
@Temporal(TemporalType.TIMESTAMP)
private Date changed;
@Column(name = "changed_by")
private String changedBy;
// ... getters/setters
}
if (entity instanceof Traceable) {
...
}Understandably, this will now always evaluate to false!Instead, we modify both the prePersist(Object) and preUpdate(Object) methods to do all the work using reflection. The next code example shows, how the methods should look like:
public class AuditLogger {
...
public void prePersist(Object entity) {
alterEntity(entity, "Created");
}
public void preUpdate(Object entity) {
alterEntity(entity, "Changed");
}
private void alterEntity(Object entity, String partialPropertyName) {
try {
Class> clazz = entity.getClass();
Method m;
String callerId = getCallerIdentity();
m = clazz.getMethod("set" + partialPropertyName, Date.class);
m.invoke(entity, new Date());
m = clazz.getMethod("set" + partialPropertyName + "By", String.class);
m.invoke(entity, callerId);
} catch (NoSuchMethodException e) {
System.out.println("Not a traceable entity.");
} catch (InvocationTargetException e) {
// unless something magic is going on in the setter, this shouldn't happen
} catch (IllegalAccessException e) {
System.err.println("Not allowed to access methods, check your security settings.");
}
}
...
}Both prePersist and preUpdate call the alterEntity(Object, String) method which, in turn, does all the magic. It fetches method descriptors for the created/createdBy/changed/changedBy properties and invokes them on the entity instance handed to the listener. Pretty simple, huh :)Well, this is it! No more is needed and it is a neat way to solve the problem. Nevertheless, I would like to finish with a word to the performance geeks amongst us (I happen to be one of them): As it may be, you might oppose to the use of reflection. However, as of Java 1.5 (and even more so in Java 1.6), reflection has basically become so fast, that the added convenience of setting the properties transparent to the guy (or gal) implementing the domain model propably outweigh the minute execution time differences between static and reflective calls.
0 comments:
Post a Comment