I am using join-fetch queries that are working fine with L2/query caching off.
However, enabling L2/query caching for these queries causes an IllegalArgumentException-object is not an instance of declaring class.
Using the debugger, I can see that the query has completed successfully, and the exception is thrown when saving the result into the cache.
The problem occurs reading the identifier via the BasicPropertyAccessor. The accessor is expecting a DarRecord instance, but it is receiving a DarRecord[] instance (with 1 valid entry) instead.
I have reviewed doco, google, bugs and forums with no success. As the query works without query caching, I cannot see how the mappings could be wrong (although I have tried many variations here). The exception seems to be within the Hibernate core - so it does not appear to be related to cache provider / database / JDBC etc.
Has anyone got any ideas?
In constructing the post - I have realised that Hibernate is also performing a subselect query after the join - not sure if this is relevant?
Hibernate version:
3.2.1 (with built in EhCacheProvider)
Mapping documents:
hibernate.cfg.xml
Code:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">@db_driver@</property>
<property name="hibernate.connection.url">@db_url@</property>
<property name="hibernate.connection.username">@db_user@</property>
<property name="hibernate.connection.password">@db_pass@</property>
<property name="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">false</property>
<property name="hibernate.generate_statistics">true</property>
<property name="c3p0.max_size">10</property>
<property name="c3p0.min_size">2</property>
<property name="c3p0.timeout">5000</property>
<property name="c3p0.max_statements">100</property>
<property name="c3p0.idle_test_period">3000</property>
<property name="c3p0.acquire_increment">2</property>
<property name="c3p0.validate">false</property>
<!-- Second-level caching -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.transaction.flush_before_completion">
true
</property>
<property name="hibernate.transaction.auto_close_session">
true
</property>
(OTHER MAPPINGS REMOVED HERE)
<mapping resource="com/xxx/DarEntity.hbm.xml"/>
<mapping resource="com/xxx/DarRecord.hbm.xml"/>
<mapping resource="com/xxx/DarField.hbm.xml"/>
<mapping resource="com/xxx/DarReference.hbm.xml"/>
</session-factory>
</hibernate-configuration>
DarRecord.hbm.xmlCode:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xxx">
<class name="DarRecord" table="DAR_RECORD">
<cache usage="read-write"/>
<id name="recordID" column="RECORD_ID" type="long">
<generator class="identity"/>
</id>
<property name="recordName" column="RECORD_NAME"/>
<property name="recordUserKey" column="RECORD_USER_KEY"/>
<bag name="fields" inverse="true" lazy="false" cascade="all,delete-orphan" fetch="join">
<key column="RECORD_ID"/>
<one-to-many class="DarField"/>
</bag>
<set name="referencedBy" lazy="true" fetch="subselect" where="TO_DISCRIMINATOR = 'REC'">
<key column="TO_KEY"/>
<one-to-many class="DarFieldReference"/>
</set>
<many-to-one name="entity" class="DarEntity" column="ENTITY_ID"/>
</class>
</hibernate-mapping>
DarField.hbm.xmlCode:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xxx">
<class name="DarField" table="DAR_FIELD">
<id name="fieldID" column="FIELD_ID" type="long">
<generator class="identity"/>
</id>
<property name="fieldName" column="FIELD_NAME"/>
<property name="fieldValue" column="FIELD_VALUE"/>
<!-- Our parent record -->
<many-to-one name="record" class="DarRecord" column="RECORD_ID"/>
<!-- Optional reference field -->
<bag name="references" lazy="false" inverse="true" cascade="all,delete-orphan" fetch="subselect">
<key column="FROM_KEY"/>
<one-to-many class="DarFieldReference"/>
</bag>
</class>
</hibernate-mapping>
DarEntity.hbm.xmlCode:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xxx">
<class name="DarEntity" table="DAR_ENTITY">
<id name="entityID" column="ENTITY_ID">
<generator class="identity" />
</id>
<property name="dataPath" column="DATAPATH"/>
<property name="repEntityType" column="REP_ENTITY_TYPE"/>
</class>
</hibernate-mapping>
DarReference.hbm.xmlCode:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xxx">
<class name="DarReference" table="DAR_REFERENCE" >
<id name="referenceID" column="REFERENCE_ID" type="long">
<generator class="identity"/>
</id>
<!-- These values (fromDiscriminator/fromKey) are set by subclasses below. -->
<discriminator column="FROM_DISCRIMINATOR" type="string" not-null="true"/>
<property name="fromKey" column="FROM_KEY" type="long" not-null="true" update="false" insert="false"/>
<!-- The target of the reference -->
<property name="toDiscriminator" column="TO_DISCRIMINATOR" type="string" not-null="true"/>
<property name="toKey" column="TO_KEY" type="long" not-null="true"/>
<!-- Subclasses of the reference (depending on the source of the reference) -->
<subclass name="DarFieldReference" discriminator-value="FLD" extends="DarReference">
<many-to-one name="field" class="DarField" column="FROM_KEY" not-null="true"/>
</subclass>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
Session session = HibernateUtil.getSession();
Query query = session.createQuery("from DarRecord DR left join fetch DR.fields DF where DR.entity.dataPath=:dataPath");
query.setString("dataPath", dataPath);
query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
// Uncomment next line to raise exception
query.setCacheable(true);
return query.list();
Full stack trace of any exception that occurs:Code:
org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of com.xxx.DarRecord.recordID
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:171)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:183)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3539)
at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:3255)
at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:181)
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:218)
at org.hibernate.type.ManyToOneType.disassemble(ManyToOneType.java:163)
at org.hibernate.cache.StandardQueryCache.put(StandardQueryCache.java:80)
at org.hibernate.loader.Loader.putResultInQueryCache(Loader.java:2185)
at org.hibernate.loader.Loader.listUsingQueryCache(Loader.java:2129)
at org.hibernate.loader.Loader.list(Loader.java:2087)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:388)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at com.xxx.DarRecordDao.getDarList(DarRecordDao.java:30)
at com.xxx.TestDarRecord.testListCouncils(TestDarRecord.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:145)
... 30 more
Name and version of the database you are using:SQL Server 2000
The generated SQL (show_sql=true):Code:
Hibernate: select darrecord0_.RECORD_ID as RECORD1_21_0_, fields1_.FIELD_ID as FIELD1_22_1_, darrecord0_.RECORD_NAME as RECORD2_21_0_, darrecord0_.RECORD_USER_KEY as RECORD3_21_0_, darrecord0_.ENTITY_ID as ENTITY4_21_0_, fields1_.FIELD_NAME as FIELD2_22_1_, fields1_.FIELD_VALUE as FIELD3_22_1_, fields1_.RECORD_ID as RECORD4_22_1_, fields1_.RECORD_ID as RECORD4_0__, fields1_.FIELD_ID as FIELD1_0__ from DAR_RECORD darrecord0_ left outer join DAR_FIELD fields1_ on darrecord0_.RECORD_ID=fields1_.RECORD_ID, DAR_ENTITY darentity2_ where darrecord0_.ENTITY_ID=darentity2_.ENTITY_ID and darentity2_.DATAPATH=?
Hibernate: select references0_.FROM_KEY as FROM3_1_, references0_.REFERENCE_ID as REFERENCE1_1_, references0_.REFERENCE_ID as REFERENCE1_23_0_, references0_.FROM_KEY as FROM3_23_0_, references0_.TO_DISCRIMINATOR as TO4_23_0_, references0_.TO_KEY as TO5_23_0_ from DAR_REFERENCE references0_ where references0_.FROM_KEY in (select fields1_.FIELD_ID from DAR_RECORD darrecord0_ left outer join DAR_FIELD fields1_ on darrecord0_.RECORD_ID=fields1_.RECORD_ID, DAR_ENTITY darentity2_ where darrecord0_.ENTITY_ID=darentity2_.ENTITY_ID and darentity2_.DATAPATH=?)
Debug level Hibernate log excerpt:Code:
- Hibernate 3.2.1
- hibernate.properties not found
- Bytecode provider name : cglib
- using JDK 1.4 java.sql.Timestamp handling
- configuring from resource: hibernate.standalone.cfg.xml
- Configuration resource: hibernate.standalone.cfg.xml
(OTHER MAPPINGS REMOVED HERE)
- Reading mappings from resource : com/xxx/DarEntity.hbm.xml
- Mapping class: com.xxx.DarEntity -> DAR_ENTITY
- Reading mappings from resource : com/xxx/DarRecord.hbm.xml
- Mapping class: com.xxx.DarRecord -> DAR_RECORD
- Reading mappings from resource : com/xxx/DarField.hbm.xml
- Mapping class: com.xxx.DarField -> DAR_FIELD
- Reading mappings from resource : com/xxx/DarReference.hbm.xml
- Mapping class: com.xxx.DarReference -> DAR_REFERENCE
- Mapping subclass: com.xxx.DarFieldReference -> DAR_REFERENCE
- Configured SessionFactory: null
- Mapping collection: com.xxx.DarRecord.fields -> DAR_FIELD
- Mapping collection: com.xxx.DarRecord.referencedBy -> DAR_REFERENCE
- Mapping collection: com.xxx.DarField.references -> DAR_REFERENCE
- C3P0 using driver: net.sourceforge.jtds.jdbc.Driver at URL: jdbc:jtds:sqlserver://XXX
- Connection properties: {user=XXX, password=****}
- autocommit mode: false
- MLog clients using log4j logging.
- Initializing c3p0-0.9.0 [built 11-July-2005 00:43:29 -0400; debug? true; trace: 10]
- Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@873723 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@1302fc5 [ acquireIncrement -> 2, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1302fc5, idleConnectionTestPeriod -> 3000, initialPoolSize -> 2, maxIdleTime -> 5000, maxPoolSize -> 10, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 2, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@e93999 [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> e93999, jdbcUrl -> jdbc:jtds:sqlserver://XXX, properties -> {user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 300, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, usesTraditionalReflectiveProxies -> false ], factoryClassLocation -> null, identityToken -> 873723, numHelperThreads -> 3 ]
- RDBMS: Microsoft SQL Server, version: 08.00.0760
- JDBC driver: jTDS Type 4 JDBC Driver for MS SQL Server and Sybase, version: 1.0.3
- Using dialect: org.hibernate.dialect.SQLServerDialect
- Using default transaction strategy (direct JDBC transactions)
- No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
- Automatic flush during beforeCompletion(): enabled
- Automatic session close at end of transaction: enabled
- Scrollable result sets: enabled
- JDBC3 getGeneratedKeys(): enabled
- Connection release mode: auto
- Default batch fetch size: 1
- Generate SQL with comments: disabled
- Order SQL updates by primary key: disabled
- Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
- Using ASTQueryTranslatorFactory
- Query language substitutions: {}
- JPA-QL strict compliance: disabled
- Second-level cache: enabled
- Query cache: enabled
- Cache provider: org.hibernate.cache.EhCacheProvider
- Optimize cache for minimal puts: disabled
- Structured second-level cache entries: disabled
- Query cache factory: org.hibernate.cache.StandardQueryCacheFactory
- Echoing all SQL to stdout
- Statistics: enabled
- Deleted entity synthetic identifier rollback: disabled
- Default entity-mode: pojo
- building session factory
- No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: jar:file:/C:/Dev/Workspaces/SB/common/lib/hibernate/ehcache-1.2.3.jar!/ehcache-failsafe.xml
- Could not find configuration [com.xxx.DarRecord]; using defaults.
- Not binding factory to JNDI, no JNDI name configured
- starting update timestamps cache at region: org.hibernate.cache.UpdateTimestampsCache
- Could not find configuration [org.hibernate.cache.UpdateTimestampsCache]; using defaults.
- starting query cache at region: org.hibernate.cache.StandardQueryCache
- Could not find configuration [org.hibernate.cache.StandardQueryCache]; using defaults.
Hibernate: select darrecord0_.RECORD_ID as RECORD1_21_0_, fields1_.FIELD_ID as FIELD1_22_1_, darrecord0_.RECORD_NAME as RECORD2_21_0_, darrecord0_.RECORD_USER_KEY as RECORD3_21_0_, darrecord0_.ENTITY_ID as ENTITY4_21_0_, fields1_.FIELD_NAME as FIELD2_22_1_, fields1_.FIELD_VALUE as FIELD3_22_1_, fields1_.RECORD_ID as RECORD4_22_1_, fields1_.RECORD_ID as RECORD4_0__, fields1_.FIELD_ID as FIELD1_0__ from DAR_RECORD darrecord0_ left outer join DAR_FIELD fields1_ on darrecord0_.RECORD_ID=fields1_.RECORD_ID, DAR_ENTITY darentity2_ where darrecord0_.ENTITY_ID=darentity2_.ENTITY_ID and darentity2_.DATAPATH=?
Hibernate: select references0_.FROM_KEY as FROM3_1_, references0_.REFERENCE_ID as REFERENCE1_1_, references0_.REFERENCE_ID as REFERENCE1_23_0_, references0_.FROM_KEY as FROM3_23_0_, references0_.TO_DISCRIMINATOR as TO4_23_0_, references0_.TO_KEY as TO5_23_0_ from DAR_REFERENCE references0_ where references0_.FROM_KEY in (select fields1_.FIELD_ID from DAR_RECORD darrecord0_ left outer join DAR_FIELD fields1_ on darrecord0_.RECORD_ID=fields1_.RECORD_ID, DAR_ENTITY darentity2_ where darrecord0_.ENTITY_ID=darentity2_.ENTITY_ID and darentity2_.DATAPATH=?)
- IllegalArgumentException in class: com.xxx.DarRecord, getter method of property: recordID