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.
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
if the field is declared in the current class (the class that declares the current method) and
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
if the field is declared in the current class and
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.
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.
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.
Related
Why Static field can't be accessed through an instance.
Dart 2.4 Flutter 1.7 Android Studio 3.4
When I was trying to port codes from JAVA to Flutter(Dart), I got the compile error
I had defined the variable in MyMainBloc as follows
static LoginStatus loginStatus = null;
Then I create an instance:
MyMainBloc myApp;
I expect to use something like this: myApp.loginStatus, NOT MyMainBloc.loginStatus.
Static members of a particular class are accessed at the class level, not the object level. What this basically means is that the static variable is shared between objects.
With that in mind, what you are potentially attempting to do is probably not the best idea. Without seeing more of your code, you are probably better off declaring your loginStatus property as a private member on an encapsulating class, then accessing that member through a method (perhaps a standard get*() method).
The Oracle documentation on class members could be of some help here.
Can anyone explain bit in detail about this rule. Rule:AccessorMethodGeneration Priority:3 Avoid autogenerated methods to access private fields and methods of inner / outer classes with concrete examples for both fields and methods?
The PMD documentation provides a summary of the AccessorMethodGeneration rule, with my emphasis added:
When accessing a private field / method from another class, the Java
compiler will generate a accessor methods with package-private
visibility. This adds overhead, and to the dex method count on
Android. This situation can be avoided by changing the visibility of
the field / method from private to package-private.
So PMD is advising you that if you make such members and methods package-private instead of private you will avoid the overhead of having to access them through compiler generated accessor methods. (I'm not an Android developer so I can't comment on the "dex method count" issue.)
This is the code example PMD provides with respect to private members:
public class OuterClass {
private int counter;
/* package */ int id;
public class InnerClass {
InnerClass() {
OuterClass.this.counter++; // wrong accessor method will be generated
}
public int getOuterClassId() {
return OuterClass.this.id; // id is package-private, no accessor method needed
}
}
}
And here is an SO example where the compiler will auto-generate code to access private methods. Note the poster's comment (with my emphasis added):
The compiler takes the inner classes and turns them into top-level
classes. Since private methods are only available to the inner class
the compiler has to add new "synthetic" methods that have package
level access so that the top-level classes have access to it.
In summary, PMD is flagging code where you can make performance improvements (which I would think would usually be very minor) in some scenarios by modifying the access on private members and private methods.
One other point worth noting is that there are some PMD bug reports on AccessorMethodGeneration not working properly (e.g. https://github.com/pmd/pmd/issues/274 and https://github.com/pmd/pmd/issues/342). So if you can't understand why PMD is flagging your code with a AccessorMethodGeneration warning, check the bug reports.
I am creating a custom CDI scope and am using the BeanManager to get an injection of my NavigationHandler custom class. But the beans it returns are quite strange.
So I use the BeanManager that way :
public class ScreenContext implements Context
{
private NavigationHandler getNavigationHandler()
{
final Set<Bean<?>> beans = m_beanManager.getBeans(NavigationHandler.class);
final Bean<?> bean = m_beanManager.resolve(beans);
NavigationHandler reference =
(NavigationHandler) m_beanManager.getReference(bean, NavigationHandler.class,
m_beanManager.createCreationalContext(bean));
System.out.println("Found "+reference+" (hash="+reference.hashCode()+")");
return reference;
}
...
}
I expect, when I use my project using two different browsers, to get two different NavigationHandler, which are defined that way :
#Named
#WindowScoped
public class NavigationHandler
implements Serializable, INavigationHandlerController
But my debugger returns true when I test reference1==reference2. I also get strange hash codes :
Found NavigationHandler#593e785f (hash=1261587818)
Found NavigationHandler#b6d51bd (hash=1261587818)
I don't understand why the hashes used in the toString() are different, but the hash used in hashCode() are the same.
I think I figured out the reason for these two linked problems, that was a tricky one !
m_beanManager.getReference(..) does not return the NavigationHandler instance, but a proxy which is supposed to select and act as the correct NavigationHandler among the existing ones in the scope's context.
Link to understand the concept of Proxy/Context/BeanManager: https://developer.jboss.org/blogs/stuartdouglas/2010/10/12/weld-cdi-and-proxies
So my getNavigationHandler() method is not suited for the work : my pool which calls this method will hold NavigationHandler proxies instead of NavigationHandlers. Because my pool is not an #Injected field, the proxy will not get automatically updated by CDI, hence the reference returned is always the one from the last context actively used by a proxy.
For the same reason in this output:
Found NavigationHandler#593e785f (hash=1261587818)
Found NavigationHandler#b6d51bd (hash=1261587818)
In one case I get the hash of the NavigationHandler instance, and in the other case I get the hash of the NavigationHandler's proxy. Yet I don't know which one is which. I am willing to believe the proxy's toString() is used, as beanManager.getReference(..) is supposed to serve a new proxy each time, and the hashCode is supposed to be practically unique for object each instances.
Link that says every instance's hashcode is unique hashcode and cannot change over time: http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode%28%29
So the correct way to implement getNavigationHandler() is:
private getNavigationHandlergetgetNavigationHandler()
{
final Set<Bean<?>> beans = m_beanManager.getBeans(getNavigationHandler.class);
final Bean<?> bean = m_beanManager.resolve(beans);
/* Works : pure reference (not proxied) */
Class<? extends Annotation> scopeType = bean.getScope();
Context context = m_beanManager.getContext(scopeType);
CreationalContext<?> creationalContext = m_beanManager.createCreationalContext(bean);
// Casts below are necessary since inheritence does not work for templates
getNavigationHandler reference =
context.get((Bean<NavigationHandler>) bean, (CreationalContext<NavigationHandler>) creationalContext);
return reference;
}
Link that explains the difference between beanManager.getReference(..) and beanManager.getContext(..).get(..): Canonical way to obtain CDI managed bean instance: BeanManager#getReference() vs Context#get()
I have some check constraints defined by SYS schema in DB for a particular column.
Now, while invoking it from Java code through MyBatis, is there anyway to enforce corresponding field length validations through MYBatis configuration only.
PS: I don't want to enforce constraints at VO level (setter individually). Or using JSR 303
DataBase : Oracle 11g
Using MyBatis
If you do not want to validate in your java beans (manually, or using JSR 303) I think you could write your own typeHandler for those field.
Typehandler would handle String fields and do validation.
See code example for String TypeHandler.
You could enforce your validation logic (of any complexity) in handler's get/set methods.
If you want to use TypeHandler to trim string to given length when saving to database, do it in setNonNullParameter method.
Sample code below
#MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.substring(0,DESIRED_MAX_LENGTH));
}
You could also trim (or otherwise modify) values you read from database- you need to modify get* method in your TypeHandler implementation to do that.
You must tell mappers to use your handler. Otherwise, default handler for given type will be used.
Your SQLs in XML file must use syntax
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
Check https://mybatis.github.io/mybatis-3/sqlmap-xml.html for details.
I have an #Embeddable class that uses property access to wrap another object that's not directly mappable by JPA via field access. It looks like this:
#Embeddable
#Access(AccessType.PROPERTY)
public class MyWrapper {
#NotNull
#Transient
private WrappedType wrappedField;
protected MyWrapper() {
}
public MyWrapper(WrappedType wrappedField) {
this.wrappedField = wrappedField;
}
#Transient
public WrappedType getWrappedField() {
return wrappedField;
}
public void setWrappedField(WrappedType wrappedField) {
this.wrappedField = wrappedField;
}
#Column(name = "wrappedTypeColumn")
protected String getJPARepresentation() {
return wrappedField.toString();
}
protected void setJPARepresentation(String jpaRepresentation) {
wrappedField = new WrappedType(jpaRepresentation);
}
}
Persisting an #Entity with a MyWrapper field works fine. But when I execute a query to load the Entity from the database, I get a NullPointerException. The stacktrace and some debugging shows that Eclipselink creates a new instance of MyWrapper by calling its default constructor and then calls the setJPARepresentation() method (as expected).
But now the unexpected happens: the stacktrace shows that the getJPARepresentation() is called from inside the setter, which then of course leads to a NullPointerException when return wrappedField.toString() is executed.
java.lang.NullPointerException
at MyWrapper.getJPARepresentation(MyWrapper.java:27)
at MyWrapper.setJPARepresentation(MyWrapper.java)
... 109 more
Fact is, there is obviously no call to the getter in the code and the stacktrace shows no line number indicating from where in the setter called the getter. So my conclusion would be, that the bytecode weaver of Eclipselink generated the call to the getter.
It's easy to build a workaround, but my question is: Why does Eclipselink do that?
P.S: I'm using EclipseLink 2.3.2.v20111125-r10461 in a GlassFish Server Open Source Edition 3.1.2 (build 23)
When weaving is enabled (default on Glassfish), EclipseLink will weave code into property get/set methods for,
change tracking
fetch groups (partial objects)
lazy (relationships)
For change tracking support the set method will be weaved to check if the new value is different than the old value, so it must call the get method to get the old value.
Now this is still odd, as since your are building a new object, I would not expect the change listener to be set yet, so would expect the change tracking check to be bypassed. You could decompile the code to see exactly what was generated.
The easiest fix is to just put in a null check in your get method, which is probably best in general for your code. You could also switch to field access, which will not have issues with side-affects in get/set methods. You could also use a Converter to handle the conversion, instead of doing the conversion in get/set methods.