支持最终字段的Java持久性提供程序

我是Java的新手,但我一直在养成使用final的习惯,尽可能宣布不变性,我认为这是一件好事。 (考虑f#)

我已经读过JPA不支持final字段。 Hibernate,TopLink? 我不确定这些,但我现在更喜欢JPA。

这在理论上是否可能 – 让我们通过反思来说 – 在创建后修改最终字段? 我的猜测是……不…

持久性解决方案当然可以支持带参数的构造函数。 至少我认为没有理由会让这一切变得不可能。 我猜,映射会有点棘手。 这是一种替代解决方案。

建议?

编辑:我不熟悉不可变的确切定义所以我直观地在这篇文章中使用它。 在此声明不变性意味着声明无法更改字段。 很抱歉对于这个误会。

对象不变性(注意不可变对象之间的区别,并声明一个字段final – 如果所有字段都是final,则对象只是不可变的,因此对象的状态在创建后不能更改)是一个非常敏感的主题。 我自己喜欢它们,而hibernate通过@Immutable支持它们。

不知道它在JPA 2中的状态,但回答有关最终字段的问题:您可以使用reflection更改其值 – 但在Java EE环境中reflection受到严重限制。

为了启发主要问题:如果您的POJO是不可变的,那么持久解决方案将如何重新创建对象? 假设您有两个最终的int字段,以及一个用于初始化它们的构造函数。 持久层不能包含有关其顺序或其名称的任何信息(因为在编译期间将删除字段和参数名称)。

Koshuke发布了一篇关于此的博客(关于支持不可变bean的JAXB),但现在找不到它。

这可能不是您正在努力的目标,但不可变性原则可以很容易地得到只有一个吸气剂的非最终私人领域的支持。 事实上,编译器会识别这一点,并生成与声明为final的字段完全相同的代码。

这将要求你尽职尽责,不要在包含类中改变你的领域(而final会强制执行),但我认为这不是为你获得的灵活性付出的代价。

好问题。 实际上,如果字段没有更改,则将字段声明为final是个好主意(然后从JMM获得初始化保证)。

JPA Spec对最终字段很清楚:

实体类不能是最终的。 实体类的方法或持久性实例变量可能不是最终的。

但是,并非所有实现都遵循此规则并处理最终字段:

  • OpenJPA不支持最终字段,即它们被视为瞬态字段。 加载后,使用无参数构造函数中定义的值初始化此类实体的最终字段。

  • 然而,Hibernate实现支持最​​终字段。 在构造时,已在无参数构造函数中初始化的最终字段将替换为存储在数据库中的值。

所以回答你的问题:是的,有JPA持久性提供程序支持final字段,但我建议不要在实体类中使用final字段,因为行为未定义(实际上不同于提供者)。 此外,我认为JPA提供商在构建后更改最终字段是一个坏主意,因为这种方式可能会违反可见性保证。

在设计创建后无法修改最终字段。 否则是一个非常糟糕的主意,因为有人可以依赖标准行为。

通常,持久性框架处理“正常”对象的层次结构。 您有对象,只能创建和删除,不能修改。 这很奇怪,因为当你创建一个实体时,你会为它创建一些ID。 通过此ID,您可以将实体连接到其他人。 当您删除旧实体并创建另一个实体时,您(通常)会获得具有另一个ID的实体。 因此,所有连接(外键)都被破坏了。 它是目标行为吗?

这是一个奇怪的想法。

通过使字段final ,您告诉编译器它们在创建对象后永远不会更改。 因此,不坚持它们是合理的假设,因为它们永远不会改变。 好吧,通过写这篇文章,我假设你有java文化,但你提出的问题恰恰相反。

在Java中,持久化的obejcts“总是”被假定为POJO(换句话说,Java Bean)。 Java Bean必须具有(被认为是这样)一个空构造函数,它将允许持久性框架等使用其空构造函数构造它,通过Class.newInstance()/间接调用它。

有些字段使用非空构造函数(如IoC容器 – Guice,Spring和Tapestry IoC),但它超出了Java Bean的范围,必须将其视为数据对象。