" {{BytecodeProviderImpl.findAccessor()}} throws an exception when called to find the accessor method for a property that is inherited:
\r\n\r\n {noformat} Caused \r\nCaused by: org.hibernate.bytecode.internal.bytebuddy.BulkAccessorException: cannot find an accessor : @0 at \r\n\tat org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl.findAccessor(BytecodeProviderImpl.java:245) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl.findAccessors(BytecodeProviderImpl.java:218) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl.getReflectionOptimizer(BytecodeProviderImpl.java:80) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:78) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_172] at \r\n\tat sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_172] at \r\n\tat sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_172] at \r\n\tat java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_172] at \r\n\tat org.hibernate.tuple.entity.EntityTuplizerFactory.constructTuplizer(EntityTuplizerFactory.java:88) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.tuple.entity.EntityTuplizerFactory.constructDefaultTuplizer(EntityTuplizerFactory.java:116) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:382) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:551) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] at \r\n\tat org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:124) ~[hibernate-core-5.3.1.Final.jar:5.3.1.Final] \r\n {noformat}
This \r\n\r\nThis is likely because it relies only on {{clazz.getDeclaredMethod( name, params )}} and nothing outside it:
\r\n\r\n {code:java} private \r\n\tprivate static Method findAccessor(Class clazz, String name, Class[] params, int index) throws \r\n\t\t\tthrows BulkAccessorException { try \r\n\t\ttry { final \r\n\t\t\tfinal Method method = clazz.getDeclaredMethod( name, params ); // <---- HERE if \r\n\t\t\tif ( Modifier.isPrivate( method.getModifiers() ) ) { throw \r\n\t\t\t\tthrow new BulkAccessorException( \ "private property \ ", index ); \r\n\t\t\t }
return \r\n\r\n\t\t\treturn method; \r\n\t\t } catch \r\n\t\tcatch (NoSuchMethodException e) { throw \r\n\t\t\tthrow new BulkAccessorException( \ "cannot find an accessor \ ", index ); \r\n\t\t } \r\n\t } \r\n {code}
The \r\n\r\nThe method names that end up passed to the above {{findAccessor()}} method are discovered based on the methods that have already been found, which brings the question as to why do they need to be found again as well. The code that discovers them before does iterate over subclasses to find them all - the following code is in {{ReflectHelper}}:
\r\n\r\n {code:java} public \r\n\tpublic static Method findGetterMethod(Class containerClass, String propertyName) { Class \r\n\t\tClass checkClass = containerClass; Method \r\n\t\tMethod getter = null;
\r\n\r\n\t\t // check containerClass, and then its super types (if any) while \r\n\t\twhile ( getter == null && checkClass != null ) { if \r\n\t\t\tif ( checkClass.equals( Object.class ) ) { break \r\n\t\t\t\tbreak ; \r\n\t\t\t }
getter \r\n\r\n\t\t\tgetter = getGetterOrNull( checkClass, propertyName );
\r\n\r\n\t\t\t // if no getter found yet, check all implemented interfaces if \r\n\t\t\tif ( getter == null ) { getter \r\n\t\t\t\tgetter = getGetterOrNull( checkClass.getInterfaces(), propertyName ); \r\n\t\t\t }
checkClass \r\n\r\n\t\t\tcheckClass = checkClass.getSuperclass(); \r\n\t\t } \r\n\t\t ... \r\n\t } \r\n {code}
But \r\n\r\nBut the code in {{PojoEntityTuplizer}} then gets their names only and passes those names on, instead of the methods themselves. The following is a relevant snippet from the {{PojoEntityTuplizer}} constructor and it has a possibly relevant \ "todo \ " comment in there:
\r\n\r\n {code:java} String \r\n\t\tString [] getterNames = new String[propertySpan]; String \r\n\t\tString [] setterNames = new String[propertySpan]; Class \r\n\t\tClass [] propTypes = new Class[propertySpan]; for \r\n\t\tfor ( int i = 0; i < propertySpan; i++ ) { getterNames \r\n\t\t\tgetterNames [i] = getters[i].getMethodName(); setterNames \r\n\t\t\tsetterNames [i] = setters[i].getMethodName(); propTypes \r\n\t\t\tpropTypes [i] = getters[i].getReturnType(); \r\n\t\t }
if \r\n\r\n\t\tif ( hasCustomAccessors || !Environment.useReflectionOptimizer() ) { optimizer \r\n\t\t\toptimizer = null; \r\n\t\t } else \r\n\t\telse { \r\n\t\t\t // todo : YUCK!!! optimizer \r\n\t\t\toptimizer = Environment.getBytecodeProvider().getReflectionOptimizer( mappedClass \r\n\t\t\t\t\tmappedClass , getterNames \r\n\t\t\t\t\tgetterNames , setterNames \r\n\t\t\t\t\tsetterNames , propTypes \r\n\t\t\t\t\tpropTypes\r\n\t\t\t ); \r\n {code}
I \r\n\r\nI cannot create a small yet comprehensive test case yet, unfortunately.
\r\n\r\n *Additional notes* \r\n # Mapping is loaded from (legacy) *.hbm.xml files. Code has no relevant annotations. \r\n # Entity bytecode is enhanced by the {{hibernate-enhance-maven-plugin}} with {{enableLazyInitialization}}, {{enableDirtyTracking}} and {{enableAssociationManagement}} set to {{true}} and {{enableExtendedEnhancement}} set to {{false}}. # I am not aware of any workarounds - I would appreciate if someone could identify them. # Despite the bytecode enhancement with lazy initialization enabled and indicated as successful ({{Successfully enhanced class [...]}}), {{BytecodeEnhancementMetadataPojoImpl}} instance for the problem class, at the time when the exception is thrown, has the {{enhancedForLazyLoading}} field set to {{false}}. I looked at the enhancer code and it seems that enhancement isn't done for unannotated classes (mapped via XML files). This may be an unrelated issue or may indicate a possibility of a workaround. \r\n# I am not aware of any workarounds - I would appreciate if someone could identify them. Annotating code (as opposed to using XML mapping) may only move the problem to compile time or it may take a different code path and \"solve it\".\r\n" |
|