-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: HQL and eager many to one relationships.
PostPosted: Mon Feb 09, 2009 9:57 pm 
Newbie

Joined: Wed Mar 05, 2008 3:11 am
Posts: 3
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 10, 2009 2:54 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
I am not very good at hibernate annotations. May be you can try using
@Fetch(JOIN) also.

_________________
Regards,
Litty Preeth


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.