The issue is related
-
composite primary key
-
javassist instrument
When a composite primary key is instrumented by using javassit, it throws the following error:
Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 13.261 sec <<< FAILURE! test01225809(com.redhat.gss.hibernate.test.EntityManagerTest) Time elapsed: 4.075 sec <<< ERROR! javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.michaelrice.data.PrimaryKey.id at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387) at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310) at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:277) at com.redhat.gss.hibernate.test.EntityManagerTest.test01225809(EntityManagerTest.java:136) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at junit.framework.TestCase.runTest(TestCase.java:176) at junit.framework.TestCase.runBare(TestCase.java:141) at junit.framework.TestResult$1.protect(TestResult.java:122) at junit.framework.TestResult.runProtected(TestResult.java:142) at junit.framework.TestResult.run(TestResult.java:125) at junit.framework.TestCase.run(TestCase.java:129) at junit.framework.TestSuite.runTest(TestSuite.java:255) at junit.framework.TestSuite.run(TestSuite.java:250) at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:84) at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62) at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140) at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127) at org.apache.maven.surefire.Surefire.run(Surefire.java:177) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345) at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009) Caused by: org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.michaelrice.data.PrimaryKey.id at org.hibernate.property.DirectPropertyAccessor$DirectGetter.get(DirectPropertyAccessor.java:62) at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue(AbstractComponentTuplizer.java:76) at org.hibernate.type.ComponentType.getPropertyValue(ComponentType.java:413) at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:254) at org.hibernate.engine.spi.EntityUniqueKey.generateHashCode(EntityUniqueKey.java:85) at org.hibernate.engine.spi.EntityUniqueKey.<init>(EntityUniqueKey.java:66) at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1719) at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1627) at org.hibernate.loader.Loader.getRow(Loader.java:1509) at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:719) at org.hibernate.loader.Loader.processResultSet(Loader.java:949) at org.hibernate.loader.Loader.doQuery(Loader.java:917) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:348) at org.hibernate.loader.Loader.doList(Loader.java:2550) at org.hibernate.loader.Loader.doList(Loader.java:2536) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2366) at org.hibernate.loader.Loader.list(Loader.java:2361) at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:495) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:357) at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:198) at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1194) at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101) at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:268) ... 24 more Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Long field com.michaelrice.data.PrimaryKey.id to org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer$1 at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168) at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55) at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36) at java.lang.reflect.Field.get(Field.java:379) at org.hibernate.property.DirectPropertyAccessor$DirectGetter.get(DirectPropertyAccessor.java:59) ... 46 more
The org.hibernate.tool.instrument.javassist.InstrumentTask is used to instument the entity classes. Here is what's used in the pom.xml: <plugin> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <phase>process-classes</phase> <goals> <goal>run</goal> </goals> </execution> </executions> <configuration> <tasks> <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask"> <classpath> <path refid="maven.runtime.classpath" /> <path refid="maven.plugin.classpath" /> </classpath> </taskdef> <instrument verbose="false"> <fileset dir="$ {project.build.outputDirectory}
"> <include name="*my_entity_classes.class" /> </fileset> </instrument> </tasks> </configuration> </plugin>
I've attached a sample project that uses EAP 6.3.0(Hibernate 4.2.14.SP1-redhat-1) to demonstrate the issue.
The EntityManagerTest class contains two test methods, test01225809 and test01225809_2. The method test01225809 shows the issue and the method test01225809_2 shows that if the composite primary key is not used the issue is gone.
It seems that when Hibernate loads the data from database and tries to bind them with the entity class, it needs to generates an unique hashCode. It has its own algorithm to create such hashCode.
public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) { int result = 17; Object[] values = getPropertyValues( x, entityMode ); for ( int i = 0; i < propertySpan; i++ ) { Object y = values[i]; result *= 37; if ( y != null ) { result += propertyTypes[i].getHashCode( y, entityMode, factory ); }
} return result; }
For the composite primary key, it needs to access each field and use its value to generate the hashCode. Consequently, it calls the filed's getter method to obtain the value. However, the javassist instrument modifies the getter method and causes error.
|