I'm seeing a weird problem with duplicate elements showing up in bags when using the Criteria API. Basically, in the example below there Project objects, which have a many-to-one relationship to Program, which itself has a many-to-many relationship to Attribute (represented as a bag). When we get a collection of projects using the Criteria API, hibernate pulls in the Program and Attribute tables with left outer joins, and the Attributes associated with each Program are duplicated as many times as there are Projects. For example, if the criteria matched 2 Projects, both of which mapped to the same Program, which itself has 3 Attributes, Program.getAttributes() would return a List of *6* Attributes rather than 3.
Note that if the Criteria query is replaced with an HQL one (as shown below), the problem is solved. Also, I know that changing the bags to sets would also solve the problem. Neither is a particularly appropriate solution in this particular situation for me, unfortunately.
Also, I know that the Criteria queries return duplicate objects - the problem here is not in the list returned by the query, but in the Collections that are set into the objects created by Hibernate.
Please let me know if you've seen anything similar and/or know the solution. Thanks!
-Dan
Hibernate version:
2.1.7
Mapping documents:
<class name="com.carbonfive.test.CalendarEntity2" table="test_calendar_entity" discriminator-value="CalendarEntity" batch-size="50">
<cache usage="read-write"/>
<id name="id" column="id" type="integer">
<generator class="native"/>
</id>
<discriminator column="discriminator"/>
<property name="name" column="name" type="string" length="255"/>
<bag name="attributes" table="test_calendar_entity_attribute_map" outer-join="true" cascade="none" batch-size="50">
<cache usage="read-write"/>
<key column="calendar_entity_id"/>
<many-to-many class="com.carbonfive.test.Attribute2" column="attribute_id"/>
</bag>
<subclass name="com.carbonfive.test.Project2" discriminator-value="Project">
<many-to-one name="program" class="com.carbonfive.test.Program2" column="program_id" cascade="none" foreign-key="FK_project_program"/>
</subclass>
<subclass name="com.carbonfive.test.Program2" discriminator-value="Program">
<bag name="projects" inverse="true" lazy="true" cascade="all" batch-size="50" >
<cache usage="read-write"/>
<key column="program_id"/>
<one-to-many class="com.carbonfive.test.Project2"/>
</bag>
</subclass>
</class>
<class name="com.carbonfive.test.Attribute2" table="test_attribute" batch-size="10">
<cache usage="read-write"/>
<id name="id" column="id" type="integer">
<meta attribute="use-in-equals">true</meta>
<generator class="native"/>
</id>
<property name="name" column="name" type="string" length="255"><meta attribute="use-in-equals">true</meta></property>
</class>
Code between sessionFactory.openSession() and session.close():
Criteria criteria = session.createCriteria(Project2.class);
criteria.add(Expression.in("id", projectids));
Collection projects = criteria.list();
/*
// doing this instead of the Criteria fixes the problem
Collection projects = HibernateUtil.getSession()
.createQuery("from Project2 p where p.id in ( :ids )")
.setParameterList("ids", projectids)
.list();
*/
Program2 program = ((Project2) projects.iterator().next()).getProgram();
assertEquals("Attributes should be unique",
new HashSet(program.getAttributes()).size(),
program.getAttributes().size());
Name and version of the database you are using:
SQL Server 8
The generated SQL (show_sql=true):
select this.id as id2_, this.program_id as program_id2_, this.name as name2_, program21_.id as id0_, program21_.name as name0_, attributes2_.calendar_entity_id as calendar1___, attributes2_.attribute_id as attribut2___, attribute23_.id as id1_, attribute23_.name as name1_ from test_calendar_entity this left outer join test_calendar_entity program21_ on this.program_id=program21_.id left outer join test_calendar_entity_attribute_map attributes2_ on program21_.id=attributes2_.calendar_entity_id left outer join test_attribute attribute23_ on attributes2_.attribute_id=attribute23_.id where this.id in (?, ?) and this.discriminator='Project'
|