Java 有没有办法为Hibernate托管对象声明最终字段?
我刚刚开始学习Hibernate,到目前为止,我看到的所有示例都与Hibernate文档中的教程非常相似:Java 有没有办法为Hibernate托管对象声明最终字段?,java,hibernate,final,pojo,Java,Hibernate,Final,Pojo,我刚刚开始学习Hibernate,到目前为止,我看到的所有示例都与Hibernate文档中的教程非常相似: package org.hibernate.tutorial.domain; import java.util.Date; public class Event { private Long id; private String title; private Date date; public Event() {} /* Accessor m
package org.hibernate.tutorial.domain;
import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
public Event() {}
/* Accessor methods... */
}
具体来说:没有一个字段被声明为final
,并且必须有一个无参数构造函数,以便Hibernate框架可以实例化类并设置其字段
但是这里有一件事-我真的不喜欢在我可以避免的情况下以任何方式使我的类可变(Java实践:不可变对象为这样做提供了一个非常有力的理由)。那么,即使我将每个字段都声明为“final”,有没有办法让Hibernate工作呢
我知道Hibernate使用反射来实例化它的类,因此需要能够调用某种构造函数,而不必冒选择错误构造函数或将错误值传递给其参数的风险,因此调用无参数构造函数并一次设置一个字段可能更安全。然而,难道不能提供必要的信息来休眠,以便它可以安全地实例化不可变的对象吗
public class Event {
private final Long id;
private final String title;
private final Date date;
public Event(@SetsProperty("id") Long id,
@SetsProperty("title") String title,
@SetsProperty("date") Date date) {
this.id = id;
this.title = title;
this.date = new Date(date.getTime());
}
/* Accessor methods... */
}
@setproperty
注释当然是虚构的,但似乎不应该超出范围。这听起来好像不是Hibernate的用例,因为它执行的许多操作都与可变状态有关:
- 合并对象
- 脏状态检查
- 冲洗变化
public class FinalEvent {
private final Integer id;
public FinalEvent(Event event) {
id = event.id;
}
}
但这确实意味着额外的工作
现在我想起来了,hibernate会话通常是线程绑定的,这至少使最终的字段安全发布的一个好处无效
您还希望最终字段有什么其他好处?不可变对象是指没有修改其状态(即字段)的方法的对象。字段不必是最终字段。因此,您可以删除所有的mutator,并将Hibernate配置为使用字段access而不是accessor,或者您可以将no-arg构造函数和mutator标记为不推荐使用。这是一种解决方法,但总比没有好。您可以通过使用构建器模式来实现所需的结果。不久前,我在Hibernate论坛上读到了关于这个想法的讨论(尽管我自己从未实现过…这个想法也困扰了我很长时间。我最近尝试的一个想法是,为模型类定义只读接口,让DAO和任何工厂在对象上返回这些接口。这意味着,即使实现是可变的,但一旦离开DAO/factory对象,就不能再对其进行调整 像这样:
public interface Grape {
public Color getColor();
}
public class HibernateGrapeDao {
public Grape findGrape(int grapeId) {
HibernateGrape grape = hibernate.find(...
return grape;
}
}
class HibernateGrape implements Grape {
....
}
甚至可能希望将实现类包保持为dao包的私有包,因此没有人可以直接处理它们。多做一点工作,但从长远来看可能有助于保持清洁。显然,在处理整个平等/身份事务时要小心。实际上,在JDK 1.5+中,hibernate可以(通过反射)处理最终字段的更改。创建一个受保护的默认构造函数(),将字段设置为某些默认值/null等。。。Hibernate可以并且将在实例化对象时覆盖这些值 这一切都是由于对Java1.5内存模型的更改而实现的,这些更改是为了启用序列化/反序列化而进行的(允许final不是final)
public class Event {
private final Long id;
private final String title;
private final Date date;
// Purely for serialization/deserialization
protected Event() {
id = null;
title = null;
date = null;
}
public Event(Long id, String title, Data date) {
this.id = id;
this.title = title;
this.date = date;
}
/* Accessor methods... */
}用@Access(AccessType.FIELD)为类添加注释,然后可以将字段设置为最终字段。像这样:
@Access(AccessType.FIELD)
public final class Event {
private final Long id;
private final String title;
private final Date date;
private Event() {
id = null;
title = null;
date = null;
}
public Event(Long id, String title, Date date) {
this.id = id;
this.title = title;
this.date = date;
}
}
+1:关于使用包装类的好主意-当需要最终类/字段时,它看起来是一个很好的解决方案。通过使可变版本仅对数据访问类可见,甚至可以避免在外部使用哪个版本(可变版本vs.包装器)的歧义。至于使用final,我的主要原因是,通常更容易保证(而不是假设)每个字段在构建时只分配一次。我们有很多数据,这些数据在使用它的JVM的整个生命周期中都是恒定的,因此澄清这一点很有帮助。最终字段可以防止用户意外地分配他们不应该拥有的内容,并引入一个微妙的错误。如果你知道某些事情永远不会改变,你就要让它成为最终的。如果您发现它应该更改,您可以随时删除该约束。@corsiKa访问器定义了该逻辑:如果您不需要更改某些属性,只需不创建setter即可。但是,我同意代码必须尽可能严格,但不能采用hibernate的方法。+1:谢谢-对于制作包装类不可能/不实用的情况,这也很有帮助(删除setter并使用基于字段的访问)。我不能说,因为我对幕后正在进行的功夫反思太激动了——私人领域实际上并不私人。有点让我觉得我应该先添加一些@readthehibernate电子书,然后再为那些不太熟悉Hibernate工作细节的用户(比如我)使用类标签。