有没有办法为Hibernate管理的对象声明最终字段?

时间:2009-05-26 18:41:17

标签: 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 methods... */
}

具体来说:没有一个字段被声明为final,并且必须有一个无参构造函数,以便Hibernate框架可以实例化该类并设置其字段。

但是事情就是这样 - 我真的不喜欢每当我能避免它时都会以任何方式使我的类变为可变(Java Practices:Immutable Objects为此做出了非常强有力的论据)。所以即使我要声明每个字段的“最终”,还有什么方法可以让Hibernate工作吗?

我理解Hibernate使用Reflection来实例化它的类,因此需要能够调用某种构造函数,而不会冒险选择错误的构造函数或将错误的值传递给它的一个参数,所以它是可能更安全地调用no-arg构造函数并一次设置一个字段。但是,是否应该可以向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... */
}

@SetsProperty注释当然是虚构的,但似乎不应该是遥不可及的。

6 个答案:

答案 0 :(得分:14)

不可变对象是指没有修改其状态(即其字段)的方法的对象。这些领域不一定是最终的。因此,您可以删除所有mutators并将Hibernate配置为使用字段访问而不是访问器,或者您可以将no-arg构造函数和mutator标记为已弃用。这有点变通,但比没有好。

答案 1 :(得分:12)

实际上在JDK 1.5+中,hibernate可以处理(通过反射)更改最终字段。创建一个受保护的默认构造函数(),将字段设置为某些默认值/ null等... Hibernate在实例化对象时可以并将覆盖这些值。

这要归功于Java 1.5内存模型的更改 - 感兴趣的更改(允许最终不是最终),以启用序列化/反序列化。

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... */

}

答案 2 :(得分:7)

这听起来好像它不是Hibernate的用例,因为它执行的许多操作都涉及可变状态:

  • 合并对象
  • 脏状态检查
  • 刷新更改

话虽这么说,如果你担心不变性,你可以选择使用copy-constructors为你的对象提供包装器:

public class FinalEvent {
    private final Integer id;

    public FinalEvent(Event event) {
        id = event.id;
    }
}

这确实意味着额外的工作。


现在我正在考虑它,hibernate会话通常是线程绑定的,这至少会使最终字段的好处 - 安全发布无效。

您正在寻找最终字段的其他好处吗?

答案 3 :(得分:4)

这个一直困扰着我很久。我最近一直在尝试的一个想法是 - 为您的模型类定义只读接口,并让您的DAO和任何工厂在对象上返回这些接口。这意味着即使实现是可变的,一旦它离开了DAO /工厂对象,它就不能再被调整了。

像这样:

public interface Grape {
  public Color getColor();
}

public class HibernateGrapeDao {
  public Grape findGrape(int grapeId) {
    HibernateGrape grape = hibernate.find(...
    return grape;
  }
}

class HibernateGrape implements Grape {
 ....
}

甚至可能希望将实现类package-private保存到dao包中,所以没有人可以直接使用它们。更多的工作,但可能有助于长期保持清洁。而且,显然,要小心整个平等/身份业务。

答案 4 :(得分:2)

您可以使用Builder模式完成所需的结果。我刚才在Hibernate论坛上读了posting讨论这个想法(尽管我自己从未实现过......)

答案 5 :(得分:0)

使用@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;
    }
}