Dear All,
I have a problem using the Criteria API with DISTINCT_ROOT_ENTITY and ScrollableResults, which I wanted to use for efficient paging of large result sets.
It seems that DISTINCT_ROOT_ENTITY is ignored then, even if I use setFetchMode(associationPath, FetchMode.JOIN).
Could you kindly help me with the right command to run to get back ScrollableResults with 4 distinct person entities with their event collections correctly populated? Run the following code, list() yields 4 - correct; using scroll() yields 11, i.e. the root transformer did not run.
Thanks,
Oren
Code:
public class LTestScrollableResults extends <somefixtureclassnotincludedhere>
{
// ========================= CONSTANTS =================================
/**
* A logger that helps identify this class' printouts.
*/
private static final Logger log = getLogger(LTestScrollableResults.class);
// ========================= DEPENDENCIES ==============================
// ========================= TESTING METHODS ===========================
/**
* Tests that <code>DISTINCT_ROOT_ENTITY</code> collocates entities in list mode.
*/
@Test
@Transactional
public void distinctRootEntityWithListResults()
{
if (log.isDebugEnabled())
{
log.debug("============== List Test ==============");
}
final GenericCriteria hibernateCriteria = personAndEventsCriteria();
final List<?> result = hibernateCriteria.list();
assertEquals(4, result.size());
}
/**
* Tests that <code>DISTINCT_ROOT_ENTITY</code> collocates entities in scroll mode.
*/
@Test
@Transactional
public void distinctRootEntityWithScrollableResults()
{
if (log.isDebugEnabled())
{
log.debug("============== ScrollableResults Test ==============");
}
final GenericCriteria hibernateCriteria = personAndEventsCriteriaScrollableResults();
final List<?> result = HibernateUtil.scrollAsList(hibernateCriteria,
ScrollMode.FORWARD_ONLY);
// Until FUR-1274 is resolved
// assertEquals(4, result.size());
assertEquals(11, result.size());
}
// ========================= PRIVATE METHODS ===========================
/**
* @return
*/
private GenericCriteria personAndEventsCriteria()
{
// Main criteria: a single criterion of ID in (result of ID sub-select)
return personCriteria(CRITERIA)
.addAlias("Event", "this.events")
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
.add(Restrictions.like("Event.eventName", "Event", MatchMode.START));
}
/**
* @return
*/
private GenericCriteria personAndEventsCriteriaScrollableResults()
{
// Main criteria: a single criterion of ID in (result of ID sub-select)
return personCriteria(CRITERIA)
.addAlias("Event", "events")
.setFetchMode("events", FetchMode.JOIN)
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
.setCacheMode(CacheMode.IGNORE)
.add(Restrictions.like("Event.eventName", "Event", MatchMode.START));
}
/**
* @param criteriaType
* @return
*/
private GenericCriteria personCriteria(final CriteriaType criteriaType)
{
return criteria(criteriaType, ComplexPersonEntity.class,
sessionFactory.getCurrentSession());
}
}
ComplexPersonEntity.java:
Code:
@Entity
@Table(name = "Person")
public class ComplexPersonEntity implements PersistentEntity<Long>
{
// ========================= CONSTANTS =================================
/**
* Serial ID.
*/
@Transient
private static final long serialVersionUID = -2801577767291743796L;
// ========================= FIELDS ====================================
@Id
@GeneratedValue
private Long id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "person", targetEntity = ComplexEventEntity.class)
private Collection<ComplexEventEntity> events;
// ========================= IMPL: PersistentEntity ====================
/**
* @return the id
*/
@Override
public Long getId()
{
return id;
}
// ========================= GET & SET =================================
/**
* @return the name
*/
public String getName()
{
return name;
}
/**
* @param name
* the name to set
*/
public void setName(final String name)
{
this.name = name;
}
/**
* @return the events
*/
public Collection<ComplexEventEntity> getEvents()
{
return events;
}
/**
* @param events
* the events to set
*/
public void setEvents(final Collection<ComplexEventEntity> events)
{
this.events = events;
}
}
ComplexEventEntity.java:
Code:
@Entity
@Table(name = "Event")
public class ComplexEventEntity implements PersistentEntity<Long>
{
// ========================= CONSTANTS =================================
/**
* Serial ID
*/
@Transient
private static final long serialVersionUID = 7578943207653776849L;
// ========================= FIELDS ====================================
/**
* Id of the event
*/
@Id
@GeneratedValue
private Long id;
/**
* The event name
*/
@Column(name = "event")
private String eventName;
@ManyToOne
@JoinColumn(name = "person_id")
private ComplexPersonEntity person;
// ========================= IMPL: PersistentEntity ====================
/**
* @return the id
*/
@Override
public Long getId()
{
return id;
}
// ========================= GET & SET =================================
/**
* @param id
* the id to set
*/
public void setId(final Long id)
{
this.id = id;
}
/**
* @return the eventName
*/
public String getEventName()
{
return eventName;
}
/**
* @param eventName
* the eventName to set
*/
public void setEventName(final String eventName)
{
this.eventName = eventName;
}
/**
* @return the person
*/
public ComplexPersonEntity getPerson()
{
return person;
}
/**
* @param person
* the person to set
*/
public void setPerson(final ComplexPersonEntity person)
{
this.person = person;
}
}
import.sql data:
Code:
INSERT INTO Person (id, name) VALUES (1, 'John Doe');
INSERT INTO Person (id, name) VALUES (2, 'John Doe');
INSERT INTO Person (id, name) VALUES (3, 'John Doe');
INSERT INTO Person (id, name) VALUES (4, 'John Doe');
INSERT INTO Person (id, name) VALUES (5, 'Jane Doe');
INSERT INTO Person (id, name) VALUES (6, 'Jack Doe');
INSERT INTO Person (id, name) VALUES (7, 'Jill Doe');
INSERT INTO Event (id, event, person_id) VALUES (1, 'Event 1', 1)
INSERT INTO Event (id, event, person_id) VALUES (2, 'Event 1', 1)
INSERT INTO Event (id, event, person_id) VALUES (3, 'Event 1', 1)
INSERT INTO Event (id, event, person_id) VALUES (4, 'Event 1', 1)
INSERT INTO Event (id, event, person_id) VALUES (5, 'Event 1', 5)
INSERT INTO Event (id, event, person_id) VALUES (6, 'Event 2', 5)
INSERT INTO Event (id, event, person_id) VALUES (7, 'Event 1', 6)
INSERT INTO Event (id, event, person_id) VALUES (8, 'Event 2', 6)
INSERT INTO Event (id, event, person_id) VALUES (9, 'Event 1', 7)
INSERT INTO Event (id, event, person_id) VALUES (10, 'Event 2', 7)
INSERT INTO Event (id, event, person_id) VALUES (11, 'Event 3', 5)