23

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?

yglodt
  • 13,807
  • 14
  • 91
  • 127
Ben
  • 532
  • 2
  • 4
  • 12

5 Answers5

39

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.

ehrhardt
  • 2,346
  • 1
  • 19
  • 13
  • 1
    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. – T3rm1 Feb 12 '14 at 12:18
  • 1
    Using `{p.*}, {b.*}` also fixes an additional subselects problem for `books`. – v.ladynev May 27 '16 at 14:12
  • 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. – dhamu Oct 29 '19 at 15:07
  • The workaround with duplicate `.addEntity` helped me. I found this question after issuing similar question with another proposals - https://stackoverflow.com/q/62554257/653539 – Tomáš Záluský Jun 24 '20 at 13:24
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.

Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171
  • 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. – Ben Aug 26 '11 at 02:03
  • 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. – Matthew Farwell Aug 26 '11 at 07:15
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!

v.ladynev
  • 19,275
  • 8
  • 46
  • 67
Ben
  • 532
  • 2
  • 4
  • 12
  • Update: correct the link. @ehrhardt answer fixes this issue for Hibernate 5.1: http://stackoverflow.com/a/17210746/3405171 – v.ladynev May 27 '16 at 14:06
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();
legendofawesomeness
  • 2,901
  • 2
  • 19
  • 32
  • 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. – Ben Aug 25 '11 at 08:26
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 it's argument. Then I constructed that manually.

Basanth Roy
  • 6,272
  • 5
  • 25
  • 25