5

我有一些像这样声明为 final 的 JPA 注释字段:

@Column(name = "SOME_FIELD", updatable = false, nullable = false)
private final String someField;

当实体插入数据库时​​,这些字段存储在数据库中。它们无法进一步更新。对于 Java 编程语言,这些字段可以被认为是最终的。

使用 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> 

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

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

因此,我觉得一些编织工具需要一些更新来满足这个 JVM 规范。EclipseLink 静态编织工具似乎就是其中之一。

问题

  • JPA 中是否允许诸如此处介绍的最终字段构造?
  • 是否有 JDK 9 选项可以禁用对其他地方的最终字段分配的检查,而不仅仅是在类的实例初始化方法()中(作为临时解决方法)?

编辑

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

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

4

3 回答 3

3

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

正如发行说明JDK-8157181中提到的,还有一个更改是为了限制编译器接受对初始化方法之外的最终字段的修改。


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

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

否则,IllegalAccessError必须抛出一个。

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

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

否则,IllegalAccesError必须抛出一个。

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

您可以按照相同的错误报告进行详细说明。


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

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

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

于 2017-09-23T05:16:20.727 回答
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 规范,并且不应将最终持久性属性放入您的实体中。

于 2017-12-12T11:30:41.543 回答
1

Eclipselink 不承认这一点,但是,我希望在我的项目中使用它。所以我做了一个肮脏的“黑客”来做到这一点。基本上,我已经覆盖了 eclipselink 类 org.eclipse.persistence.internal.jpa.weaving.ClassWeaver 并添加了这个方法:

        @Override
public FieldVisitor visitField(
        final int access,
        final String name,
        final String descriptor,
        final String signature,
        final Object value) {
    if (cv != null) {
        int newAccess = access;
        if (!Modifier.isStatic(access) && Modifier.isFinal(access)) {
            newAccess = access & (~Opcodes.ACC_FINAL);
        }
        return cv.visitField(newAccess, name, descriptor, signature, value);
    }
    return null;
}

这将在编织时从实体上的非静态字段中删除所有最终修饰符。

我认为 eclipselink 应该考虑添加这个,至少作为一个选项。

于 2018-04-04T22:32:30.360 回答