eclipselink static weaving with final fields on Java 9

不打扰是莪最后的温柔 提交于 2021-02-19 01:56:41

问题


I have some JPA annotated fields declared final like this:

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

Those fields are stored in the database when the entity is inserted in the database. They cannot further be updated. For the Java programming language, such fields can be considered final.

With the EclipseLink static weaving plugin, it's possible to lazy load entities, owing to some byte code instrumentation.

I don't know if such constructs (final fields) are officially allowed in JPA, but I like to use them, since they enforce at compile time that these fields are not meant to be updated.

In Java 8, programs built with such constructs run fine. But in Java 9, when the EclipseLink static weaving is involved, I get the following runtime exception:

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> 

Such an error seems to be due to the following JVM specification:

Otherwise, if the field is final, it must be declared in the current class, and the instruction must occur in an instance initialization method () of the current class. Otherwise, an IllegalAccessError is thrown.

Therefore, I feel like some weaving tools need some update to fulfill this JVM specification. The EclipseLink static weaving tool seems to be one of them.

Questions:

  • Are final field constructs such as those presented here allowed in JPA?
  • Is there a JDK 9 option to disable the check for final field assignment elsewhere than only in the instance initialization method() of the class (as a temporary workaround)?

Edit:

Final fields are not allowed in JPA, as per JPA specifications:

The entity class must not be final. No methods or persistent instance variables of the entity class may be final.


回答1:


Please read JPA specification - http://download.oracle.com/otndocs/jcp/persistence-2_2-mrel-eval-spec/index.html - Section 2.1 The Entity Class:

The entity class must not be final. No methods or persistent instance variables of the entity class may be final.

Method _persistence_set is code added by weaving (bytecode manipulation of entity classes) and is used to initialize values of entity attributes after class was created by default constructor (with no attributes) call. Java 1.8 allowed this even with final fields, but Java 9 does not.

You should now follow JPA specification and should not put final persistence attributes into your entities.




回答2:


Are final field constructs such as those presented here allowed in JPA?

As mentioned in the release note JDK-8157181 as well there is a change brought to restrict Compilers to accept modification of final fields outside initializer methods.


According to the Java VM Specification, the putstatic bytecode is allowed to modify a final field only

  1. if the field is declared in the current class (the class that declares the current method) and
  2. if the putstatic instruction appears in the class or interfaceinitializer method <clinit> of the current class.

Otherwise, an IllegalAccessError must be thrown.

Similarly, the putfield bytecode is allowed to modify a final field only

  1. if the field is declared in the current class and
  2. if the instruction appears in an instance initializer method of the current class.

Otherwise, an IllegalAccesError must be thrown.

Methods that do not satisfy condition (2) violate the assumptions of the compilers. and have been kept under a check to implement the desired condition with Java 9.

You can follow the BugReport over the same for a detailed explanation.


Is there a JDK 9 option to disable the check for final field assignment elsewhere than only in the instance initialization method() of the class (as a temporary workaround)?

With the JDK 9 release, the HotSpot VM fully enforces the previously mentioned restrictions, but only for class files with version number >= 53(Java 9). For class files with version numbers < 53, restrictions are only partially enforced (as it is done by releases preceding JDK 9). That is, for class files with version number < 53 final fields can be modified in any method of the class declaring the field (not only class/instance initializers).

So, you can try compiling your code with source and target 1.8 to check if that resolves the issue for now. This should be doable with --release 8 option using the latest javac tool.




回答3:


Eclipselink does not admit it, however, I wanted this for my project. So I did a dirty "hack" to be able to do it. Basically, I have overwritten eclipselink class org.eclipse.persistence.internal.jpa.weaving.ClassWeaver and I have added this method:

        @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;
}

Which will remove all final modifiers from non static fields on entities when weaving.

I think eclipselink should consider adding this, at least as an option.



来源:https://stackoverflow.com/questions/46376045/eclipselink-static-weaving-with-final-fields-on-java-9

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!