I'm having a problem (which could just be a missunderstanding about hql and hibernate) when I do a HQL query on an entity that has an eagerly loaded relationship.
I'm doing a HQL query on Parent entity which has an eagerly loaded many-to-one relationship to a Child entity. However, I am not specifing this relationship in my hql query.
What I expected to see was a single sql statement issued on the Parent entity. However, for each Parent entity in the resultSet a query was issued for it's Child. So my single query turns into a n+1 query.
Is this the correct behaviour? Is it impossible to query an entity out without loading its relationships that have been specified as eager?
See below for a self contained example.
Here is the simple test I have.
First the domain objects.
This is the parent entity which has the eager many to one relationship with the child entity.
Code:
package gekko.scratch.tje.hql;
import ...(conserving space)
@Entity
@Table(name = "eg_eager_many_to_one_parent")
public class ParentEagerManyToOne {
private ChildEagerManyToOne child;
private String name;
private Long id;
@Id
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.EAGER, cascade = {})
@JoinColumn(name = "n_key_g")
public ChildEagerManyToOne getEagerManyToOneChild() {
return child;
}
public void setEagerManyToOneChild(ChildEagerManyToOne child) {
this.child = child;
}
@GekkoMin(value="1")
@GekkoMax(value="255")
@Column(name="t_f_name", nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now the child entity
Code:
package gekko.scratch.tje.hql;
import ...
@Entity
@Table(name = "eg_eager_many_to_one_child")
public class ChildEagerManyToOne {
private String name;
private Long id;
@Id
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@GekkoMin(value="1")
@GekkoMax(value="255")
@Column(name="t_g_name", nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now my test:
Obviously you'll need your own datasource etc to reproduce this.
Code:
package gekko.scratch.tje.hql;
import java.util.List;
import ...
public class HqlQueryServiceTest extends SharedContainerTest {
private static final Logger log = LogManager
.getLogger(HqlQueryServiceTest.class);
@PersistenceContext
private EntityManager em;
@Autowired
private TestObjects testObjects;
@Autowired
private transient DataSource dataSource;
@Test
public void testQueryResultsWithEagerJoin() {
Ejb3Configuration cfg = new Ejb3Configuration();
cfg.setDataSource(dataSource);
cfg.setProperty("hibernate.hbm2ddl.auto", "");
cfg.setProperty("hibernate.jdbc.batch_size", "0");
cfg.addAnnotatedClass(ParentEagerManyToOne.class);
cfg.addAnnotatedClass(ChildEagerManyToOne.class);
log.debug("building EMF");
EntityManagerFactory emf = cfg.buildEntityManagerFactory();
log.debug("EMF built");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ParentEagerManyToOne f = new ParentEagerManyToOne();
f.setId(Long.parseLong(testObjects.createUniqueNumber()));
f.setName("fPrefix" + testObjects.createUniqueNumber());
ChildEagerManyToOne child = new ChildEagerManyToOne();
child.setId(Long.parseLong(testObjects.createUniqueNumber()));
child.setName("gname");
f.setEagerManyToOneChild(child);
em.persist(child);
em.persist(f);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
log.debug("testQueryResultsWithEagerJoin(): before query.");
Query q = em.createQuery(
"Select parent from ParentEagerManyToOne as parent" +
" where parent.name like 'fPrefix%'");
List<?> resultList = q.getResultList();
log.debug("testQueryResultsWithEagerJoin(): after, count: " + resultList.size());
em.getTransaction().commit();
em.close();
}
}
Now, what I thought would happen is I would get 1 sql statement issued along the lines of
Code:
select * from ParentEagerManyToOne where name like 'fPrefix%';
I do get the above sql being executed but after that I get n queries that do
Code:
select * from ChildEagerManyToOne where id=?
So for each Parent that is retrieved a query for its child is being executed.