有什么办法可以为Hibernate管理的对象声明最终字段吗?

问题描述:

我刚开始使用Hibernate和所有的例子,我看到这么远看很像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:不可变对象为这样做是一个非常强大的论据)。所以是否有任何方法让Hibernate工作,即使我要声明每个字段的最终'

据我所知,Hibernate使用Reflection来实例化它的类,因此需要能够调用某种构造函数,而不用冒着选择错误构造函数或将错误值传递给其参数之一的风险调用无参数构造函数并逐个设置每个字段可能更安全。但是,应该不可能向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注释当然是虚构的,但似乎并不像它应该是遥不可及的。

这听起来像它不是一个用例Hibernate的,因为许多操作它执行的关注可变的状态:

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

这就是说,如果你担心不变性,你可以选择在你的物体周围提供包装,使用复制构造函数:

public class FinalEvent { 
    private final Integer id; 

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

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


现在我想起来了,冬眠的会议通常是线装书,这空隙至少一个最终场的优势 - 安全的出版物。

还有什么其他的最终领域的好处是你在寻找什么?

+1

+1:使用包装类的好主意 - 它看起来像是一个很好的解决方案,当需要最终的类/字段时。甚至可以通过使可变版本仅对数据访问类可见,避免在外部使用哪个版本(可变与包装)的歧义。 至于使用final,我的主要原因是保证(而不是假设)每个字段在构造时只分配一次通常更容易。我们有很多数据意味着在使用它的JVM的整个生命周期中保持不变,因此清楚地说明这一点很有帮助。 – 2009-05-28 14:22:03

+1

最终字段可防止用户意外地分配他们不应该拥有的内容,并引入一个细微的错误。如果你知道某件事情永远不会改变,那么你想让它最终成为现实。如果你发现它应该改变,你可以随时消除这个限制。 – corsiKa 2015-05-26 16:58:02

不可变对象是指没有方法修改其状态(即其字段)的对象。这些字段不一定是最终的。所以你可以删除所有的mutators,并配置Hibernate使用字段acces而不是访问器,或者你可以将no-arg构造函数和mutators标记为不建议使用。这有点解决方法,但比没有更好。

您可以通过使用Builder模式来实现所期望的结果。前段时间我在Hibernate论坛上读到posting讨论这个想法(尽管我自己从来没有实现过......)

这个问题一直在困扰着我很长一段时间。最近我一直在尝试的一个想法是 - 为你的模型类定义只读接口,让你的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 { 
.... 
} 

甚至可能希望保留的实现类包私人的刀包,所以没有人可以直接与他们摆弄。多一点工作,但可能有助于长期保持清洁。显然,要小心整个平等/身份认证业务。

实际上,在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... */ 

}

注释与@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; 
    } 
}