I'm using Play Framework, with Hibernate as the JPA provider, and I noticed that if an entity has a Blob member, it will always get flushed to the DB, even if I haven't changed anything in it, and even if I don't read the Blob's value.
Blob is a UserType defined in Play. Here's the source code. The basic idea of the class is to save the actual data in the file system, and only save a pointer (a UUID) in the DB table.
From this SO answer, I gather that something in the Blob code must be making a change between the time it gets loaded from the DB and the time Hibernate inspects it. There's also this answer that suggests it might be something else.
How can I change the Blob class to avoid the DB flush if no changes are made?
GreyBeardedGeek's answer proved correct. The current equals implementation returns true only on objects with the same identity, and always returns false on nulls.
Changing this:
public boolean equals(Object o, Object o1) throws HibernateException {
return o == null ? false : o.equals(o1);
}
to this:
private static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
public boolean equals(Object a, Object b) throws HibernateException {
if(a instanceof Blob && b instanceof Blob) {
return equal(((Blob)a).UUID, ((Blob)b).UUID) &&
equal(((Blob)a).type, ((Blob)b).type);
}
return equal(a, b);
}
made all the excessive DB updates go away.
You'd have to actually debug this to find out, but I suspect a problem with hashcode()
and equals()
. The first thing that I'd check is the implementation of hashcode()
and equals()
in the class that you are persisting with the Blob UserType - make sure that they work consistently, and follow the rules - .e.g. if equals()
returns false when it is used to compare the 'old' and the 'new' state of your object, it's going to get persisted.