24

我刚刚开始使用 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 实践:不可变对象为此做了一个非常有力的论据)。那么,即使我将每个字段都声明为“final”,是否有任何方法可以让 Hibernate 正常工作

我知道 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... */
}

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

4

6 回答 6

13

不可变对象是指没有修改其状态(即其字段)的方法的对象。字段不必是最终的。因此,您可以删除所有修改器并将 Hibernate 配置为使用字段访问而不是访问器,或者您可以将无参数构造函数和修改器标记为已弃用。这有点解决方法,但总比没有好。

于 2009-05-28T11:19:54.470 回答
13

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

这一切都是可能的,这要归功于 Java 1.5 内存模型的更改——为了启用序列化/反序列化而进行的感兴趣的更改(允许 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... */

}

于 2010-09-10T09:18:11.480 回答
8

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

  • 合并对象
  • 脏状态检查
  • 冲洗变化

话虽如此,如果您担心不可变性,您可以选择使用复制构造函数为对象提供包装器:

public class FinalEvent {
    private final Integer id;

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

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


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

您还在寻找 final 字段的其他好处吗?

于 2009-05-28T10:34:49.243 回答
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 {
 ....
}

甚至可能希望将实现类包私有到 dao 包,所以没有人可以直接摆弄它们。多做一些工作,但从长远来看可能有助于保持清洁。而且,显然,要小心整个平等/身份事务。

于 2009-12-24T15:50:29.270 回答
2

您可以通过使用 Builder 模式来实现所需的结果。我不久前在 Hibernate 论坛上阅读了一篇讨论这个想法的帖子(尽管我自己从未实现过它......)

于 2009-05-28T14:56:54.033 回答
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;
    }
}
于 2014-08-13T13:37:40.677 回答