Hello Hibernate Community.
So I have made a more detailed test case, in the hope of getting more (some) help.
The Database is Oracle 11, all though there isn't much oracle specific stuff happening here. I have simplified the SQL / Hibernate mappings a little.
Test1 - 2 tables, using a single column for the primary / foreign key:
Code:
CREATE TABLE "SCHEMA"."TEST1PARENT"
(
"TEST1PARENT" VARCHAR2(20 BYTE),
"CREATING_USER" NVARCHAR2(255),
"CREATING_DATE" DATE,
"CHANGING_USER" NVARCHAR2(255),
"CHANGING_DATE" DATE,
CONSTRAINT "PK_TEST1PARENT" PRIMARY KEY ("TEST1PARENT")
)
TABLESPACE "USERS" ;
Code:
CREATE TABLE "SCHEMA"."TEST1CHILD"
(
"TEST1PARENT" VARCHAR2(20 BYTE),
"TEST1CHILD" VARCHAR2(20 BYTE),
...
CONSTRAINT "PK_TEST1CHILD" PRIMARY KEY ("TEST1CHILD") ,
CONSTRAINT "FK_TEST1CHILD_PARENT" FOREIGN KEY ("TEST1PARENT") REFERENCES "TEST1PARENT" ("TEST1PARENT") ON DELETE CASCADE ENABLE
)
TABLESPACE "USERS" ;
Now the Hibernate mappings :
Code:
<hibernate-mapping>
<class name="datatypes.Test1child" table="TEST1CHILD" schema="SCHEMA">
<id name="test1child" type="string">
<column name="TEST1CHILD" length="20" />
<generator class="assigned" />
</id>
<many-to-one name="test1parent" class="datatypes.Test1parent" fetch="select">
<column name="TEST1PARENT" length="20" />
</many-to-one>
<property name="creatingUser" type="string">
<column name="CREATING_USER" />
</property>
<property name="creatingDate" type="date">
<column name="CREATING_DATE" length="7" />
</property>
<property name="changingUser" type="string">
<column name="CHANGING_USER" />
</property>
<property name="changingDate" type="date">
<column name="CHANGING_DATE" length="7" />
</property>
</class>
</hibernate-mapping>
Code:
<hibernate-mapping>
<class name="datatypes.Test1parent" table="TEST1PARENT" schema="SCHEMA">
<id name="test1parent" type="string">
<column name="TEST1PARENT" length="20" />
<generator class="assigned" />
</id>
...
<set name="test1childs" inverse="true">
<key>
<column name="TEST1PARENT" length="20" />
</key>
<one-to-many class="datatypes.Test1child" />
</set>
</class>
</hibernate-mapping>
The tables have been filled with a few test rows. Nothing of particular interest.
Code:
Criteria criteria = session.createCriteria(Test1child.class);
criteria.add(Restrictions.like("test1child", "2"));
//works correctly with simple foreign keys (1 column)
criteria.setProjection(Property.forName("test1parent"));//doesn't work for complex key
Collection<Test1parent> results = criteria.list();
//everything works fine
//basic tests
for (Test1parent par : results)
{
System.err.println(par.getTest1parent());
for (Test1child child : par.getTest1childs())
{
System.err.println(child.getTest1child());
}
}
Now when I do the same for a complex key, criteria seems to get into trouble?
Again my SQL, Hibernate mappings and Java code.
Code:
CREATE TABLE "TEST2PARENT"
(
"TEST2PARENT" VARCHAR2(20 BYTE),
"TEST2COMP2" VARCHAR2(20 BYTE),
...
CONSTRAINT "PK_TEST2PARENT" PRIMARY KEY ("TEST2PARENT", "TEST2COMP2")
)
TABLESPACE "USERS" ;
Code:
CREATE TABLE "TEST2CHILD"
(
"TEST2PARENT" VARCHAR2(20 BYTE),
"TEST2COMP2" VARCHAR2(20 BYTE),
"TEST2CHILD" VARCHAR2(20 BYTE),
...
CONSTRAINT "PK_TEST2CHILD" PRIMARY KEY ("TEST2CHILD", "TEST2COMP2") ,
CONSTRAINT "FK_TEST2CHILD_PARENT" FOREIGN KEY ("TEST2PARENT", "TEST2COMP2") REFERENCES "TEST2PARENT" ("TEST2PARENT", "TEST2COMP2") ON DELETE CASCADE ENABLE
)
TABLESPACE "USERS" ;
so big difference is complex primary and (hence) complex foreign keys. Not really a problem for SQL or even HQL.
Code:
<hibernate-mapping>
<class name="datatypes.Test2parent" table="TEST2PARENT" schema="SCHEMA">
<composite-id name="id" class="datatypes.Test2parentId">
<key-property name="test2parent" type="string">
<column name="TEST2PARENT" length="20" />
</key-property>
<key-property name="test2comp2" type="string">
<column name="TEST2COMP2" length="20" />
</key-property>
</composite-id>
...
<set name="test2childs" inverse="true">
<key>
<column name="TEST2PARENT" length="20" />
<column name="TEST2COMP2" length="20" not-null="true" />
</key>
<one-to-many class="datatypes.Test2child" />
</set>
</class>
</hibernate-mapping>
Code:
<hibernate-mapping>
<class name="datatypes.Test2child" table="TEST2CHILD" schema="SCHEMA">
<composite-id name="id" class="datatypes.Test2childId">
<key-property name="test2child" type="string">
<column name="TEST2CHILD" length="20" />
</key-property>
<key-property name="test2comp2" type="string">
<column name="TEST2COMP2" length="20" />
</key-property>
</composite-id>
<many-to-one name="test2parent" class="datatypes.Test2parent" update="false" insert="false" fetch="select">
<column name="TEST2PARENT" length="20" />
<column name="TEST2COMP2" length="20" not-null="true" />
</many-to-one>
...
</class>
</hibernate-mapping>
And now almost the same criteria command, which generates the exception, why is that?
Code:
Criteria criteria2 = session.createCriteria(Test2child.class);
criteria2.add(Restrictions.like("test2child", "100"));
criteria2.setProjection(Property.forName("test2parent"));//doesn't work for complex key
//org.hibernate.QueryException: property does not map to a single column: id :(
Collection<Test2parent> results2 = criteria2.list();
for (Test2parent par : results2)
{
System.err.println(par.getId().getTest2parent()+":"+par.getId().getTest2comp2());
for (Test2child child : par.getTest2childs())
{
System.err.println(child.getId().getTest2child()+":"+child.getId().getTest2comp2());
}
}
It throws the following exception
Code:
org.hibernate.QueryException: property does not map to a single column: test2parent
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getColumn(CriteriaQueryTranslator.java:371)
at org.hibernate.criterion.PropertyProjection.toSqlString(PropertyProjection.java:41)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getSelect(CriteriaQueryTranslator.java:310)
at org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:71)
at org.hibernate.loader.criteria.CriteriaLoader.<init>(CriteriaLoader.java:67)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1550)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
at de.MyTestCode
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:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:422)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:931)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:785)
I'd really appreciate any help here.
How can I return an a complex key (id) from a criteria expression?
Thanks in advance
Martin