0

Here is my code

public class Start {
    public static void main(String[] args) {
        AuthorHelper authorHelper = new AuthorHelper();

        Author author1 = authorHelper.getAuthor(4L);
        author1.setName("TEST");
        authorHelper.updateAuthor(author1);
        

        List<Author> authorList = authorHelper.getAuthorList();

        authorList.forEach(author -> {
            System.out.println(author.getName());
            System.out.println("Books");
            System.out.println("--------------------------------------------------------------------------------");

            
            author.getBooks().forEach(book1 -> {
                System.out.println(book1.getTitle());
            });

        });



    }
}

When I am trying to get books from author i get this:

Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: entity.Author.books: could not initialize proxy - no Session

Here is my getAuthorList method:

public List<Author> getAuthorList() {
        List<Author> authorList;
        Session session = sessionFactory.openSession();

        authorList = session.createQuery("SELECT a FROM Author a", Author.class).list();

        session.close();

        return authorList;
    }

I tried to use JOIN FETCH, but if I undersand it correctly - hibernate will fetch all authors and all books from db, but thats not what i want, because it is not always necessarily to get books from authors. I think the problem is in List because it contains detached entities and no Session open. I also tried to use

Hibernate.initialize(author.getBooks());

Before getting books, but it is still not working, any ideas how to fix that?

denstran
  • 43
  • 6
  • hibernate achive lazy loading through proxy. If you have already closed the session, it can not fetch the proxy data anymore and you need to again make the author entity managed. If you dont want to necessarily fetch the book list, better make a check inside the session and fetch accordingly. – rahulP Jul 16 '23 at 07:54

2 Answers2

0

I tried to use JOIN FETCH, but if I understand it correctly - hibernate will fetch all authors and all books from db, but thats not what i want, because it is not always necessarily to get books from authors

Just keep using JOIN fetch in getAuthorList(). The thing is your query use case in the end still need to get all books for every author you fetched , why you don't want to fetch them at the first place ?

If you still insist don't want to use JOIN FETCH , you have to extend the transaction boundary such that the session is still open when you access the books for the author. Something like :

public void getAuthorList(Consumer<List<Author>> authorConsumer) {
        List<Author> authorList;
        Session session = sessionFactory.openSession();
        session.getTransaction().begin();

        authorList = session.createQuery("SELECT a FROM Author a", Author.class).list();

        authorConsumer.accept(authorList);
        session.getTransaction().commit();
        session.close();

    }

And call it by :


        List<Author> authorList = authorHelper.getAuthorList(author -> {
            System.out.println(author.getName());
            System.out.println("Books");
            System.out.println("-----------------");
            
            author.getBooks().forEach(book1 -> {
                System.out.println(book1.getTitle());
            });

        });

But if you do that , you will have N+1 query problem.

Understand your concern that maybe there are another query use cases that you just want to get all authors without fetching any books. So ideally your data access layer should have two version of get authors method. One is use JOIN FETCH to fetch books together with author (e.g. getAuthorListWithBook()) while another just fetch authors without fetching any books (e.g. getAuthorList()). And let the clients to choose which to use depending on if their usecase need to access book.

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
-1

It looks like relation between Author and Book entities is OneToMany, and by default fetch type is Lazy.

So when you start your loop to iterate books hibernate is trying to select books (in lazy mode) but session was closed at the end of getAuthorList.

To fix I can suggest you setup that relation with FetchType.EAGER or close session somewhere after you finish your logic.

  • But FetchType.EAGER will do the same as FETCH JOIN, thats not what i want, and where can i close session if not in getAuthorList method? Maybe i am doing something wrong? – denstran Jul 15 '23 at 20:58
  • So open your session befor all your logic and close right after it. – Kirill Byvshev Jul 15 '23 at 21:14