Hibernate 原生 SQL 查询检索实体和集合

23

这是我的情况,我有两个基本的POJO,我已经给它们一个简单的Hibernate映射:

Person
  - PersonId
  - Name
  - Books

Book
  - Code
  - Description

我的MySQL查询返回的行长这样:

PERSONID NAME       CODE DESCRIPTION
-------- ---------- ---- -----------
1        BEN        1234 BOOK 1
1        BEN        5678 BOOK 2
2        JOHN       9012 BOOK 3

我的 Hibernate 查询代码如下:

session.createSQLQuery("select personid, name, code, description from person_books")  
       .addEntity("person", Person.class)
       .addJoin("book", "person.books")
       .list();

这是根据Hibernate文档中的18.1.3节: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/querysql.html#d0e17464

我期望在我的列表中得到2个人对象,每个人对象都包含一组书籍对象:

List
 |- Ben
 |   |- Book 1
 |   '- Book 2
 '- John
     '- Book 3

我实际看到的是这样的:

List
 |- Object[]
 |   |- Ben
 |   |   |- Book 1
 |   |   '- Book 2
 |   '- Book 1
 |- Object[]
 |   |- Ben
 |   |   |- Book 1
 |   |   '- Book 2
 |   '- Book 2
 '- Object[]
     |- John
     |   '- Book 3
     '- Book 3

有人知道是否可以使用这种方法获得我想要的内容吗?

5个回答

39

扩展 Mathews 的回答。 要强制 Hibernate 仅返回人员列表,请执行以下操作:

List<Person> peopleWithBooks = session.createSQLQuery(
   "select {p.*}, {b.*} from person p, book b where <complicated join>").
     .addEntity("p", Person.class)
     .addJoin("b", "p.books")
     .addEntity("p", Person.class)
     .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
     .list();
Associated Book entities will be fetched and initialized without an additional call to the database.
The duplicate
 .addEntity("p", Person.class)

是必要的,因为

 .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)

操作最后添加的实体。


1
这将获取Person和Book的所有内容。如果您想选择仅某些属性以提高性能,该怎么办?据我所知,除了*或p.*之外的任何内容都不起作用。 - T3rm1
1
使用 {p.*},{b.*} 还可以解决 books 的附加子查询问题。 - v.ladynev
这个函数每个人只获取一本书,无论这个人有多少本书?我有一个类似的问题并使用了这个解决方案。它为“joined”实体返回一个集合,但并没有返回其中的所有值。集合大小为1。 - dhamu
使用重复的.addEntity方法解决了我的问题。在提出其他建议的类似问题后,我发现了这个问题 - https://stackoverflow.com/q/62554257/653539 - Tomáš Záluský

13

以下方法对我有效:

session.createSQLQuery("select p.*, b.* from person p, book b where <complicated join>").
.addEntity("person", Person.class).addJoin("book", "person.books").list();

这会返回一个包含Person列表的Object[],每个Person包含一个Book列表。它通过一次 SQL 查询完成。我认为你的问题在于你没有将 person 明确地命名为任何别名。

编辑:该方法返回一个Object[],但数组中填充的是Person实例,且只有Person实例。

如果 Hibernate 不知道如何映射到你的类,或者无法理解如何映射连接关系,则会返回一个对象列表。确保每行只有一个Person/Book组合。


我期望得到的是一个Person列表,而不是一个Object[]列表,其中包含重复的人,当他们拥有多本书时。请参阅我的答案,了解hibernate jira错误报告的链接。 - Ben
抱歉,让我再说清楚一点,该方法返回一个Object[],但数组中只填充了Person,而且只有Person。里面没有任何书籍。 - Matthew Farwell

7

更新:修正链接。@ehrhardt的答案解决了Hibernate 5.1的问题:https://dev59.com/n2w05IYBdhLWcg3whCNn#17210746 - v.ladynev

1

你的查询应该是针对 person 表而不是 person_books 表吗?

session.createSQLQuery("select * from person")  
   .addEntity("person", Person.class)
   .addJoin("book", "person.books")
   .list();

我为了简洁起见简化了这个例子。在我的实际使用中,“person_books”是一个复杂的查询,Hibernate无法在没有我提供查询信息的情况下获取“book”的信息。我正在检索大量的Person行,并希望不必执行额外的查询来查找每个Person的Books。如果我找不到解决方案,我可能会使用这种方法,但只跳过Person未更改的行。 - Ben

1
据我所知,从SQL查询中无法获取“合并”的实体。您只会得到一个对象数组。在这种情况下,我的做法是创建一个新的构造函数来构建我的合并实体,该构造函数以对象数组作为参数。然后我手动构建它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接