Hibernate нетерпеливая загрузка (выборка всех свойств не работает) - PullRequest
4 голосов
/ 18 апреля 2011

В основном я хочу загружать свойства. У меня есть следующий запрос HQL:

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

Я ожидаю, что это выполнит только один запрос. Вместо этого я получил N + 1 запросов.

Код следующий:

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);
}

Вывод, который я получаю (с hibernate.show_sql, установленным на true):

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: Ситуация такая же без трансформаторов .

<Ч />

Edit:

Файл с сопоставлениями сущностей:

<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>

Я попытался добавить lazy="false" к классу и к отдельным свойствам. Без разницы.

Мой файл конфигурации:

<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:

Даже следующий код вызывает N + 1 запрос. Хотя я получаю только поле идентификатора, которое согласно документации не должно вызывать загрузку объектов.

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

Ответы [ 3 ]

8 голосов
/ 23 апреля 2011

Проблема была с .iterate(). Согласно Hibernate API документам :

Объекты, возвращаемые как результаты, инициализируются по требованию. Первый запрос SQL возвращает только идентификаторы.

Это специальная функция, которая будет использоваться, когда мы ожидаем, что результирующие объекты будут уже кэшированы . Они будут (ленивыми) загружаться при доступе.

Так что для общего использования, чтобы получить итератор для результата запроса, вы должны использовать .list().iterate().

Спасибо за Эран Харел за помощь.

0 голосов
/ 18 апреля 2011

Похоже, он не может получить объект в одном запросе с отдельными столбцами.

Поскольку вы используете те же значения из User для других столбцов, пример из @axtavt кажется вашим лучшим решением.

0 голосов
/ 18 апреля 2011

В сущности, причиной вашей проблемы является попытка включить логику преобразования результата в запрос.

Я думаю, что было бы лучше сделать простой запрос HQL, а затем применить вашу конкретную логику преобразования результата впользовательский ResultTransformer, что-то вроде этого:

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;
        }
    }); 
...