Darlyn
Darlyn

Reputation: 4938

Using new Constructor in hql query cannot find proper consturctor or could not extract ResultSet

I have entities:

@Entity
public class Author{
    @ManyToMany(cascade = { CascadeType.ALL })
    @JoinTable(
            name = "author_book",
            joinColumns = { @JoinColumn(name = "author_id") },
            inverseJoinColumns = { @JoinColumn(name = "book_id") }
    )
   public List<Book> books;
}

@Entity
public class Book{
   @ManyToMany(mappedBy = "books")
   public List<Author> authors;

   @OneToMany(mappedBy = "book")
   public List<Fact> facts;

   public Book(){}
   public Book(List<Author> authors, List<Fact> facts){
       this.authors = authors;
       this.facts = facts;
   }
}

@Entity class Fact{
   @ManyToOne
   @JoinColumn(name="book_id")
   public Book book;
}

Book has more Authors and Authors can write more books. Every book has more facts about it and each act belongs only to one book.

Now what i want achieve is to recieve object of type Book based on author ID. The object of type Book should containt all authors and all facts.

I am using hibernate query for this:

List<Book> books = (List<Book>) session.createQuery("Select new Book (books.authors, books.facts ) FROM Author u JOIN u.books books JOIN books.facts WHERE u.id IN :ids ").setParameter("ids", authorId).list();

However it results in error:

Unable to locate appropriate constructor on class [Book]. Expected arguments are: java.util.Collection, java.util.Collection

I have tried to adjust constructor to take Object or Collection, and cast it by myself:

   public Book(Object authors,Object facts){
       this.authors = (List<Author>)authors;
       this.facts = (List<Fact>)facts;
   }

But this complains:

org.hibernate.exception.SQLGrammarException: could not extract ResultSet

What is the right way to fetch entity with wanted data then? It Complains only about relations (e.g collections), i have not found any solution for this anywhere ( nor documentation for hql ).

Thanks for help!

Upvotes: 2

Views: 1316

Answers (1)

Simon Martinelli
Simon Martinelli

Reputation: 36133

You cannot use the NEW operator with Entities!

This is for DTO (Data Transfer Object) projection.

In your case you don't even need the NEW because you can simply select the book which will be constructed by Hibernate using the default constructor and setting the fields.

List<Book> books = 
   (List<Book>) session.createQuery(
   "Select books FROM Author u JOIN u.books books JOIN books.facts WHERE u.id IN :ids ")
   .setParameter("ids", authorId).list();

and if you want to have the collections eager loaded use JOIN FETCH like:

List<Book> books = 
   (List<Book>) session.createQuery(
   "Select b FROM Book b JOIN FETCH b.authors a JOIN FETCH b.facts f WHERE a.id IN :ids ")
   .setParameter("ids", authorId).list();

Important notice:

Hibernate cannot fetch two Lists eagerly. There for I suggest to change the Lists to Sets:

@ManyToMany(mappedBy = "books")
public Set<Author> authors;

@OneToMany(mappedBy = "book")
public Set<Fact> facts;

Upvotes: 3

Related Questions