Hibernate代理对象不适用于超类方法

时间:2011-01-13 07:06:47

标签: java hibernate javassist mappedsuperclass

我们有一个使用Hibernate的Web应用程序。在将代码库升级到Hibernate 3.6(从3.3.2开始)后,我发现Hibernate生成的代理数据对象只返回某些方法的正确值。似乎具体数据模型类中的方法工作正常,但@MappedSuperclass抽象超类中的方法不起作用。

以下是我们的数据模型:

@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

}

这在Hibernate 3.3.2中运行正常,但在升级到Hibernate 3.6后,应用程序出错了。以下测试代码说明了问题:

    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());

该代码的输出是:

id = 1234
getId() = 0
getSecurityId() = 182
getVersion() = 0
getLastUpdateDate() = null
getCreatedDate() = null
getName() = Form name here
getPath() = /path/here

其中四种方法返回了错误的结果:getId(),getVersion(),getLastUpdateDate()和getCreatedDate()返回0或null。数据库中的实际行具有非零/非空值。但是getName(),getPath()和最好奇的getSecurityId()都运行良好。

任何人都可以解释为什么会这样吗?这是映射超类的一个基本问题,还是有另一个原因导致这种情况发生?

请注意,Hibernate返回的Form对象是Javassist代理 - 如果在调试器中查看,它通常具有类Form_$$_javassist_15等类名。

<小时/> 更新

这个问题似乎发生在Hibernate,而不是Javassist。我通过在hibernate.properties中设置hibernate.bytecode.provider=cglib将字节码生成切换到CGLIB,但是在CGLIB到位时获得完全相同的错误结果(并且确认CGLIB正在工作,因为Hibernate返回的类名变为Form$$EnhancerByCGLIB$$4f3b4523 )。

尽管如此,我仍然无法确定为什么会出错。

1 个答案:

答案 0 :(得分:4)

我找到了答案:超类上的一些getter和setter被标记为final

事后看来这很明显......因为方法是最终的,代理类无法覆盖它们。因此,解决方案是从映射的超类中的任何getter和setter中删除final

以下是SecuredDataObject上定义的getter和setter:

@MappedSuperclass
public abstract class SecuredDataObject extends DataObject {

    @Version
    @Column(name = "Version")
    private int version;

    @Basic
    @Column(name = "SecurityId", nullable = true)
    private Integer securityId;

    // Note - method IS NOT final
    public Integer getSecurityId() {
        return securityId;
    }

    public void setSecurityId(Integer securityId) {
        this.securityId = securityId;
    }   

    // Note - method IS final
    public final int getVersion() {
        return version;
    }

    public final void setVersion(final int version) {
        this.version = version;
    }
}

它解释了为什么我的测试正确地返回securityId但未正确返回version - getVersion()final,而getSecurityId()则没有。{/ p >

总而言之,如果Hibernate可能尝试代理它们,请不要将getter和setter标记为final