eclipselink静态编织与Java 9上的最终字段

Sté*_*cel 5 jpa eclipselink compile-time-weaving java-9

我有一些JPA注释字段,如下所示:

@Column(name = "SOME_FIELD", updatable = false, nullable = false)
private final String someField;
Run Code Online (Sandbox Code Playgroud)

当实体插入数据库时​​,这些字段存储在数据库中.它们无法进一步更新.对于Java编程语言,可以将这些字段视为final.

使用EclipseLink静态编织插件,由于某些字节代码检测,可能会延迟加载实体.

我不知道JPA中是否正式允许这样的构造(最终字段),但我喜欢使用它们,因为它们在编译时强制执行这些字段并不是要更新.

在Java 8中,使用这种结构构建的程序运行良好.但是在Java 9中,当涉及EclipseLink静态编织时,我得到以下运行时异常:

Caused by: java.lang.IllegalAccessError: Update to non-static final field xxx.yyy.SomeEntity.someField attempted from a different method (_persistence_set) than the initializer method <init> 
Run Code Online (Sandbox Code Playgroud)

这样的错误似乎是由于以下JVM规范:

否则,如果该字段是final,则必须在当前类中声明,并且该指令必须在当前类的实例初始化方法()中发生.否则,抛出IllegalAccessError.

因此,我觉得一些编织工具需要一些更新才能实现这个JVM规范.EclipseLink静态编织工具似乎就是其中之一.

问题:

  • JPA中是否允许使用此处提供的最终字段构造?
  • 是否有JDK 9选项来禁止检查最终字段分配,而不仅仅是在类的实例初始化方法()中(作为临时解决方法)?

编辑:

根据JPA规范,JPA中不允许使用最终字段:

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

Nam*_*man 5

JPA 中是否允许使用此处介绍的最终字段构造?

正如发行说明JDK-8157181中提到的,还进行了一项更改,以限制编译器接受初始化程序方法之外的最终字段的修改。


根据Java VM规范,putstatic字节码只允许修改一个final字段

  1. 如果该字段是在当前类(声明当前方法的类)中声明的并且
  2. 如果putstatic指令出现在当前类classinterface初始化方法中。<clinit>

否则,IllegalAccessError必须抛出一个。

同样,putfield字节码只允许修改一个final字段

  1. 如果该字段在当前类中声明并且
  2. 如果指令出现在当前类的实例初始化方法中。

否则,IllegalAccesError必须抛出一个。

不满足条件 (2) 的方法违反了编译器的假设。并一直在检查以使用 Java 9 实现所需的条件。

您可以关注BugReport以获得详细说明。


是否有 JDK 9 选项可以在类的实例初始化 method() 之外的其他地方禁用对最终字段分配的检查(作为临时解决方法)?

在 JDK 9 版本中,HotSpot VM 完全强制执行前面提到的限制,但仅适用于版本号 >= 53 (Java 9) 的类文件。对于版本号 < 53 的类文件,仅部分强制执行限制(正如 JDK 9 之前的版本所做的那样)。也就是说,对于版本号 < 53 的 final类文件,可以在声明该字段的类的任何方法中修改字段(不仅是类/实例初始值设定项)

因此,您可以尝试使用源和目标 1.8 编译代码,以检查是否可以暂时解决问题。这应该可以通过--release 8使用最新javac工具的选项来实现。

  • 发行说明 @nullpointer 引用可能确实是问题所在,在这种情况下,编译为“--release 8”可能是解决方法。有人应该检查 EclipseLink bug 数据库,也许它已经被修复了(自 2016 年中期以来,JDK 9 的 EA 版本已通过此更改提供)。 (2认同)

小智 1

请阅读 JPA 规范 - http://download.oracle.com/otndocs/jcp/persistence-2_2-mrel-eval-spec/index.html - 第 2.1 节实体类:

实体类不能是最终的。实体类的任何方法或持久实例变量都不能是最终的。

方法 _persistence_set 是通过编织(实体类的字节码操作)添加的代码,用于在通过默认构造函数(无属性)调用创建类后初始化实体属性的值。Java 1.8 即使对于 Final 字段也允许这样做,但 Java 9 不允许。

您现在应该遵循 JPA 规范,并且不应将最终持久性属性放入实体中。