Hi,
we are using composite-ids for the entities. I know this isn't recommned but it's mandatory for our application.
We want to map relations only with a part of the composite-id.
The mapping for 1:1-relations works fine, but 1:n doesn't work.
Here's the mapping:
Code:
<?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="de.jbfagree.history.test.onetomany">
<class name="Person" table="person">
<!-- composite-id -->
<composite-id>
<key-property name="oid" column="oid" />
<key-property name="knownOn" column="known_on" type="long" />
</composite-id>
<!-- mapped oid from composite-id -->
<property name="oid" column="oid" update="false" insert="false" />
<!-- field -->
<property name="unknownOn" column="unknown_on" type="long" />
<!-- field -->
<property name="name" type="text" />
<!-- one-to-many to Account -->
<bag name="accounts" cascade="all">
<!-- only use oid-field as foreign-key reference -->
<!-- unique foreign key from person.oid on account.person_id -->
<key column="person_id" property-ref="oid" not-null="true" update="false"/>
<one-to-many class="Account" />
</bag>
</class>
<class name="Account" table="account">
<!-- composite-id -->
<composite-id>
<key-property name="oid" column="oid" />
<key-property name="knownOn" column="known_on" type="long" />
</composite-id>
<!-- mapped oid from composite-id -->
<property name="oid" column="oid" update="false" insert="false" />
<!-- field -->
<property name="unknownOn" column="unknown_on" type="long" />
<!-- field -->
<property name="accountNr" type="int" />
</class>
</hibernate-mapping>
The exception depends on how the one-to-many is mapped.
If the one-to-many key is mapped with "update=false not-null=true" the following error occurs during save:
Code:
java.lang.ClassCastException: de.jbfagree.history.test.onetomany.Person
at org.hibernate.type.StringType.toString(StringType.java:67)
at org.hibernate.type.NullableType.nullSafeToString(NullableType.java:117)
at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:158)
at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:131)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2025)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2271)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2688)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at de.jbfagree.history.test.AbstractJPLTest.save(AbstractJPLTest.java:70)
at de.jbfagree.history.test.onetomany.TestTemporalOneToMany.testCreateSingleRevision(TestTemporalOneToMany.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:164)
at junit.framework.TestCase.runBare(TestCase.java:130)
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:120)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
The insert executed from hibernate:
Code:
insert
into
account
(unknown_on, accountNr, person_id, oid, known_on)
values
(?, ?, ?, ?, ?)
If the one-to-many key is mapped with "update=true not-null=false" the objetcs get correctly persisted but an error occurs during load:
Code:
16:05:17,323 ERROR BasicPropertyAccessor:191 - IllegalArgumentException in class: de.jbfagree.history.key.TemporalModel, getter method of property: oid
16:05:17,323 INFO SessionFactoryImpl:805 - closing
16:05:17,323 INFO DriverManagerConnectionProvider:170 - cleaning up connection pool: jdbc:mysql://localhost/bps
org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of de.jbfagree.history.key.TemporalModel.oid
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:195)
at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue(AbstractComponentTuplizer.java:87)
at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValues(AbstractComponentTuplizer.java:93)
at org.hibernate.tuple.component.PojoComponentTuplizer.getPropertyValues(PojoComponentTuplizer.java:109)
at org.hibernate.type.ComponentType.getPropertyValues(ComponentType.java:376)
at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:207)
at org.hibernate.engine.EntityKey.generateHashCode(EntityKey.java:126)
at org.hibernate.engine.EntityKey.<init>(EntityKey.java:70)
at org.hibernate.engine.StatefulPersistenceContext.getCollectionOwner(StatefulPersistenceContext.java:701)
at org.hibernate.engine.loading.CollectionLoadContext.getLoadingCollection(CollectionLoadContext.java:130)
at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1026)
at org.hibernate.loader.Loader.readCollectionElements(Loader.java:669)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:614)
at org.hibernate.loader.Loader.doQuery(Loader.java:724)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.doList(Loader.java:2228)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125)
at org.hibernate.loader.Loader.list(Loader.java:2120)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:401)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:361)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:196)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1148)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
at org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:835)
at de.jbfagree.history.test.onetomany.TestTemporalOneToMany.getPerson(TestTemporalOneToMany.java:105)
at de.jbfagree.history.test.onetomany.TestTemporalOneToMany.testCreateSingleRevision(TestTemporalOneToMany.java:86)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:164)
at junit.framework.TestCase.runBare(TestCase.java:130)
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:120)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.lang.IllegalArgumentException: java.lang.ClassCastException@16c9867
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:169)
... 41 more
The select executed from hibernate:
Code:
select
person0_.oid as oid0_0_,
person0_.known_on as known2_0_0_,
accounts1_.oid as oid1_1_,
accounts1_.known_on as known2_1_1_,
person0_.unknown_on as unknown3_0_0_,
person0_.name as name0_0_,
accounts1_.unknown_on as unknown3_1_1_,
accounts1_.accountNr as accountNr1_1_,
accounts1_.person_id as person5_0__,
accounts1_.oid as oid0__,
accounts1_.known_on as known2_0__
from
person person0_
inner join
account accounts1_
on person0_.oid=accounts1_.person_id
where
person0_.oid=?
Here are the classes:
TemporalModel.java (Superclass)
Code:
package de.jbfagree.history.key;
import java.io.Serializable;
public abstract class TemporalModel implements Serializable {
private String oid;
private Long knownOn;
private Long unknownOn;
public String getOid() {
return oid;
}
public void setOid(String oid) {
this.oid = oid;
}
public Long getKnownOn() {
return knownOn;
}
public void setKnownOn(Long knownOn) {
this.knownOn = knownOn;
}
public Long getUnknownOn() {
return unknownOn;
}
public void setUnknownOn(Long unknownOn) {
this.unknownOn = unknownOn;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((knownOn == null) ? 0 : knownOn.hashCode());
result = prime * result + ((oid == null) ? 0 : oid.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TemporalModel other = (TemporalModel) obj;
if (knownOn == null) {
if (other.knownOn != null)
return false;
} else if (!knownOn.equals(other.knownOn))
return false;
if (oid == null) {
if (other.oid != null)
return false;
} else if (!oid.equals(other.oid))
return false;
return true;
}
}
Person.java
Code:
package de.jbfagree.history.test.onetomany;
import java.util.ArrayList;
import java.util.List;
import de.jbfagree.history.Versioned;
import de.jbfagree.history.VersioningType;
import de.jbfagree.history.key.TemporalModel;
public class Person extends TemporalModel {
private String name;
private List<Account> accounts;
public Person() {
accounts = new ArrayList<Account>();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
}
Account.java
Code:
package de.jbfagree.history.test.onetomany;
import de.jbfagree.history.Versioned;
import de.jbfagree.history.VersioningType;
import de.jbfagree.history.key.TemporalModel;
public class Account extends TemporalModel {
private int accountNr;
public int getAccountNr() {
return accountNr;
}
public void setAccountNr(int accountNr) {
this.accountNr = accountNr;
}
}
The hibernate-configuration
Code:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="foo">
<property name="hibernate.connection.password">pass</property>
<property name="hibernate.connection.username">user</property>
<property name="hibernate.connection.url">jdbc:mysql://<url> </property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<mapping resource="model.hbm.xml" />
</session-factory>
</hibernate-configuration>
I know why the error occurs but I don't know how to fix it. According to documentation this should work fine.
We can't change the type of mapping. Also, it's not possible to use a bidirectional one-to-many.
Is this a bug or a "not really supported feature"?
Thanks for help, kind regards
sk