开发者

Hibernate eager loading (fetch all properties does not work)

Basically I want to eager-load properties. I have the following HQL query:

SELECT u.id AS id, u.name AS text, u AS obj FROM User AS u fetch all properties

I would expect this to execute one query only. Instead I got N+1 queries.

The code is the following:

Query q = mySession.createQuery(
    "SELECT u.id AS id, u.name AS text, u AS obj FROM User AS u fetch all properties")
    .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);

for (Iterator i = q.iterate(); i.hasNext();) {
    Object line = i.next();
    System.out.println(line);
}

The output I get (with hibernate.show_sql set to true) is:

Hibernate: select user0_.id as col_0_0_, user0_.name as col_1_0_, user0_.id as col_2_0_ from user user0_
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.location as location0_0_ from user user0_ where user0_.id=?
{id=1, obj=User@b6548 [id='1' name='John' ], text=John}
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.location as location0_0_ from user user0_ where user0_.id=?
{id=2, obj=User@4865ce [id='2' name='Arnold' ], text=Arnold}

Ps: The situation is just the same without transformers.


Edit:

The file with the entity-mappings:

<hibernate-mapping>
    
    <class name="User" table="user">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        <property name="location"/>
        <map name="customPrices" table="custprice">
            <key column="user"/>
            <map-key-many-to-many column="product" class="Product"/>
            <element column="price" type="double"/>
        </map>
    </class>
    
    <class name="Product" table="product">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        <property name="listprice"/>
    </class>
    
</hibernate-mapping>

I tried adding lazy="false" to the class and to the individual properties. No difference.

My configuration file:

<hibernate-configuration>
<session-factory>
    <property name="connection.url">jdbc:mysql://192.168.0.203/hibtest</property>
    <property name="connection.username">hibtest</property>
    <property name="connection.password">hibb345</property>
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
    <property name="current_session_context_class">thread</property>
    <property name="hibernate.show_sql">true</property>
    
    <mapping resource="combined.hbm.xml" />
</session-factory> 
</hibernate-configuration>

Edit2:

Even the following code causes N+开发者_如何学C1 query. Although I only fetch the ID field, which according to documentation should not cause objects to load.

for (Iterator i = q.iterate(); i.hasNext();) {
    Object line = i.next();
    User u = (User)((Map)line).get("obj");
    System.out.println(u.getId());
}


The problem was with .iterate(). According to Hibernate API docs:

Entities returned as results are initialized on demand. The first SQL query returns identifiers only.

This is a special function to be used when we expect the resulting objects to be already cached. They will be (lazy) loaded when accessed.

So for general use, to get an iterator on a result of a query you should use .list().iterate().

Thanks for Eran Harel for the help.


In essense, the cause of your problem is an attempt to incorporate result transformation logic into a query.

I think it would be better to make a simple HQL query and then apply your specific result transformation logic in a custom ResultTransformer, something like this:

Query q = mySession.createQuery("FROM User")
    .setResultTransformer(new ResultTransformer() {
        public List transformList(List l) { return l; }
        public Object transformTuple(Object[] tuple, String[] aliases) {
            Map<String, Object> r = new HashMap<String, Object>();
            User u = (User) tuple[0];
            r.put("obj", u);
            r.put("id", u.getId());
            r.put("text", u.getName());
            return r;
        }
    }); 


Looks like it cannot retrieve the obj in a single query with separate columns.

Since you use the same values from User for the other columns, the example from @axtavt seems your best solution.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜