We have a web application that uses Hibernate. After upgrading the codebase to Hibernate 3.6 (from 3.3.2) I've found that the proxy data objects generated by Hibernate only return the correct value for some methods. It seems that methods in a concrete data model class work fine, but methods in @MappedSuperclass
abstract superclasses are not working.
Here is the data model we have:
@MappedSuperclass
public abstract class DataObject implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, columnDefinition = "serial")
private int id;
// getter, setter, equals, hashcode implementations here
}
@MappedSuperclass
public abstract class SecuredDataObject extends DataObject {
@Version
@Column(name = "Version")
private int version;
@Basic
@Column(name = "SecurityId", nullable = true)
private Integer securityId;
// getters, setters here
}
@MappedSuperclass
public abstract class AuditedDataObject extends SecuredDataObject {
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CreatedDate", nullable = true)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "LastUpdateDate", nullable = true)
private Date lastUpdateDate;
// getters, setters here
}
@Entity
@Table(name = "Form")
public class Form extends AuditedDataObject {
@Basic
@Column(name = "Name", length = 200, nullable = false)
private String name;
@Basic
@Column(name = "Path", length = 80, nullable = true)
private String path;
// getters, setters, other properties and methods here
}
This worked OK in Hibernate 3.3.2, but after the upgrade to Hibernate 3.6 the application has gone awry. The following test code illustrates the problem:
int formId = 1234;
Form form = (Form) sessionFactory.getCurrentSession().load(Form.class, formId);
System.out.print("id = ");
System.out.println(formId);
System.out.print("getId() = ");
System.out.println(form.getId());
System.out.print("getSecurityId() = ");
System.out.println(form.getSecurityId());
System.out.print("getVersion() = ");
System.out.println(form.getVersion());
System.out.print("getLastUpdateDate() = ");
System.out.println(form.getLastUpdateDate());
System.out.print("getCreatedDate() = ");
System.out.println(form.getCreatedDate());
System.out.print("getName() = ");
System.out.println(form.getName());
System.out.print("getPath() = ");
System.out.println(form.getPath());
The output of that code is:
id = 1234
getId() = 0
getSecurityId() = 182
getVersion() = 0
getLastUpdateDate() = null
getCreatedDate() = null
getName() = Form name here
getPath() = /path/here
Four of those methods have returned incorrect results: getId(), getVersion(), getLastUpdateDate() and getCreatedDate() have returned 0 or null. The actual row in the database has non-zero / non-null values. However getName(), getPath() and most curiously getSecurityId() have worked fine.
Can anyone explain why this is happening? Is it a fundamental problem with mapped superclasses, or is there another reason why this could occur?
Note that the Form
object returned by Hibernate is a Javassist proxy - if viewed in a debugger it typically has a class name like Form_$$_javassist_15
etc.
Update:
This problem seems to be occurring in Hibernate, not Javassist. I switched over the bytecode generation to CGLIB by setting hibernate.bytecode.provider=cglib
in hibernate.properties, but get exactly the same erroneous results with CGLIB in place (and confirmed CGLIB is working because the class name returned by Hibernate becomes Form$$EnhancerByCGLIB$$4f3b4523
).
I'm still no closer to identifying why it's going wrong, though.