Hibernate Native SQL Query retrieving entities and collections
Asked Answered
M

5

24

This is my situation, I have two basic POJO's which I've given a simple hibernate mapping :

Person
  - PersonId
  - Name
  - Books

Book
  - Code
  - Description

My SQL Query returns rows that look like this :

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

My hibernate query looks like this :

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

This is per section : 18.1.3 of the hibernate documentation : http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/querysql.html#d0e17464

What I expect to get in my list is 2 Person Objects with the contained book objects in the collection of books :

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

What I am actually seeing is this :

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

Does anyone know if it's possible to get what I want using this method?

Macaco answered 25/8, 2011 at 7:51 Comment(0)
M
7

HHH-2831 Native SQL queries with addJoin or return object arrays instead of single Entities

This behaviour is caused by a known bug. Doh, should have searched harder!

Macaco answered 26/8, 2011 at 2:1 Comment(1)
Update: correct the link. @Lousewort answer fixes this issue for Hibernate 5.1: https://mcmap.net/q/544097/-hibernate-native-sql-query-retrieving-entities-and-collectionsAmbition
L
40

Expanding on Mathews answer. To force hibernate to only return a list of persons do:

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 a additional call to the db.

The duplicate

 .addEntity("p", Person.class)

is necessary because

 .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)

operates on the last entity added.

Lousewort answered 20/6, 2013 at 10:0 Comment(4)
This would fetch everything for Person and Book. What if you wanted to select only certain properties to improve performance. As I understand anything other than * or p.* does not work.Turpin
Using {p.*}, {b.*} also fixes an additional subselects problem for books.Ambition
Does this fetch only a single book per person, regardless the no of books associated with a person? I have a similar problem and used this solution. It returns a set for the 'joined' entity, however doesn't return all the values in it. Set size is 1.Inalterable
The workaround with duplicate .addEntity helped me. I found this question after issuing similar question with another proposals - https://mcmap.net/q/581987/-hibernate-native-sql-query-how-to-get-distinct-root-entities-with-eagerly-initialized-one-to-many-association/653539Mikes
H
13

The following works for me:

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

This returns an Object[] containing a list of Person, each of which contains a list of Books. It does this in a single SQL select. I think your problem is that you don't specifically alias person to anything.

EDIT: The method returns an Object[], but the array is populated with Person instances, and only Person instances.

If Hibernate doesn't understand how to map to your classes or if it can't understand how to map the join, it will return a list of objects. Make sure you only have one Person/Book combination on each line.

Hickey answered 25/8, 2011 at 11:40 Comment(2)
What I was expecting was a list of Person, not a list of Object[] with duplicates of people, when they own more than one book. See my answer for the link to the hibernate jira bug report.Macaco
Sorry, to make myself clear, the method returns an Object[], but the array is populated with Person, and only Person. I don't have any books in there.Hickey
M
7

HHH-2831 Native SQL queries with addJoin or return object arrays instead of single Entities

This behaviour is caused by a known bug. Doh, should have searched harder!

Macaco answered 26/8, 2011 at 2:1 Comment(1)
Update: correct the link. @Lousewort answer fixes this issue for Hibernate 5.1: https://mcmap.net/q/544097/-hibernate-native-sql-query-retrieving-entities-and-collectionsAmbition
W
1

Should your query be on the person table instead of person_books?

session.createSQLQuery("select * from person")  
   .addEntity("person", Person.class)
   .addJoin("book", "person.books")
   .list();
Williswillison answered 25/8, 2011 at 8:7 Comment(1)
I have simplified this example for the sake of being concise. In my actual usage "person_books" is a complicated query, hibernate has no way of getting the information for a "book" without me providing it in the query. I am retrieving a large number of rows for Person and would prefer to not have to perform an extra query to find the Books for every Person. If I can't find a solution I will probably be using this method but just skipping over rows where the Person doesn't change.Macaco
R
1

AFAIK, it is not possible to get a "merged" entity back from a SQL query. You will get back only an object array. What I did in this situation was that I created a new constructor for my merged entity that took an array of objects as its argument. Then I constructed that manually.

Repro answered 26/8, 2011 at 2:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.