Announcement Announcement Module
Collapse
No announcement yet.
[Spring Data JPA] ManyToMany, rg.hibernate.LazyInitializationException, design Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • [Spring Data JPA] ManyToMany, rg.hibernate.LazyInitializationException, design

    Hi, I have a ManyToMany relationship between two Entities (A and C), then our famous LazyInitializationException appears, after googling a lot, coding JPA directly, using EAGER (cannot simultaneously fetch multiple bags, sets instead of lists, ...), ... I think my best solution is to have 3 entities:
    A, B and C, where B only has 2 fields (ManyToOne to A and C). It works great!

    What's your opinion?

    Thanks in advance.

  • #2
    If such link or bridge table would has its own properties, do it, if not, in what moment you got the LazyInitializationException?
    Could you post your annotation configuration?

    Read Hibernate documentation for to get better information

    Comment


    • #3
      Hi, with this configuration:

      Code:
      @Entity
      public class C {
      	@ManyToMany
      	private List<a> list;
      ...
      }
      the problem appears when I want to add a new element, I explain:

      Code:
      	c.getList().getElements().add(a);
      	repository.save(c);
      with the EAGER fetching I don't have this problem, but others appears.
      Another option is:

      Code:
      	aList.add(a);
      	c.setList(aList);
      	repository.save(c);
      But this produces delete and inserts.

      Thanks.
      Last edited by venosov; Mar 12th, 2012, 02:50 AM.

      Comment


      • #4
        If you have a webapp, and you dont want the eager loading in most cases just a few, and holding the transaction open is undesirable you can look into OpenEntityManagerInViewFilter.

        Comment


        • #5
          Originally posted by wgorder View Post
          If you have a webapp, and you dont want the eager loading in most cases just a few, and holding the transaction open is undesirable you can look into OpenEntityManagerInViewFilter.
          Thanks wgorder, but it isn't a webapp.

          Comment


          • #6
            Ok I will give you a couple other alternatives, this would be for the scenario where eager loading the relationship all the time is not what you want.

            Option 1-
            One common way of doing this all though it does look somewhat strange is to trigger the lazy load by accessing the field or relationship before the entity becomes detached.

            On a collection valued relationship usually calling .size() on the collection will have the desired effect. If you are not dealing with a collection say you have an Employee on your object, you would probably need to call getEmployee.getName(). This is because the entity returned from the lazy loading is actually a proxy that waits until a method is invoked on it before the entity is faulted in. So calling just getEmployee() would not have the desired effect as you would only have the proxy loaded at that point.

            Option 2-
            You could look at fetch joins to eagerly fetch your data as part of the query. If you are fetching collections you have to be careful for duplicate results. You would either need to put these in a Set to eliminate the duplicates or use the Distinct keyword. Using the distinct keyword (assuming JPQL not possible in SQL) will cause the provider to eliminate the duplicates in memory which may hurt performance for large collections so be aware of that.

            http://www.objectdb.com/java/jpa/que...ER_JOIN_FETCH_
            http://stackoverflow.com/questions/5...ing-join-fetch

            Option 3-
            If you move away from JPA and look at the vendor specific implementations you can usually do somethingn like batch fetching.

            http://docs.jboss.org/hibernate/orm/...fetching-batch

            Comment


            • #7
              Thanks wgorder, I know how to resolve this with JPA (your option 1), for example:

              Code:
                 public List fetchReservationsWithRelationships()
                 {
                    List list = manager.createQuery("FROM Reservation res").getResultList();
                    for (Object obj : list)
                    {
                       Reservation res = (Reservation)obj;
                       res.getCabins().size();
                       res.getCustomers().size();
                    }
                    return list;
                 }
              
                 // instead of
              
                 public List fetchReservations()
                 {
                    return manager.createQuery("FROM Reservation res").getResultList();
                 }
              but I'd like to use Spring Data JPA, therefore, I think the solution is to have 3 entities.

              Comment


              • #8
                venosov,

                There is nothing wrong with the 3 entity approach, so I am not trying to deter you. But I am curious why you cant do that with spring data?

                Do you mean you don't want to extend your repository and provide an impl for that find, but rather just want to annotate with @query and be done with it?

                Thanks,

                Comment


                • #9
                  Thanks wgorder, I'm curious too, this is the objective of this thread :-). I extended my repository with the same result:

                  Code:
                      @Override
                      @Transactional
                      public void addElement(C c, A a) {
                          c.getList().getElements().add(a);
                          em.persist(c);
                      }
                  in other words, I haven't yet seen an example of Spring Data JPA that add an element in a ManyToMany relationship.

                  Please, if you want more details, ask me, I think this is a great exercise.

                  Thanks again.
                  Last edited by venosov; Mar 12th, 2012, 10:43 AM.

                  Comment


                  • #10
                    The phenomenon you see here has got nothing to do with Spring Data at all but is a 101 of working with an OR mapper: accessing lazily loaded references you need to have the entity associated with a Session/EntityManager which has to be open. With Spring a Session/EntityManager is bound to the transaction scope by default. Spring Data repositories implicitly trigger transactions at their method boundaries if no transaction is in place already. Thus this is opening an EntityManager which get's closed on finishing the method call. If OpenEntityManagerInView is not an option the only reasonable way is to span the transaction more coarse grained:

                    Code:
                    class RepoClient {
                    
                      private final MyRepository repo;
                    
                      public RepoClient(MyRepository repo) {
                        Assert.notNull(repo);
                        this.repo = repo;
                      }
                    
                      @Transactional(readOnly = true)
                      public void myMethod() {
                         Entity entity = repo.findBySomething();
                         // access all properties of the entity here
                      }
                    }
                    Triggering the start of the transaction will cause the repository to transparently take part in it and keep the underlying EntityManager open so that you can access all lazy properties of entity until you return from myMethod().

                    Comment


                    • mohini
                      mohini commented
                      Editing a comment
                      Hi Oliver Gierke,

                      I am having the same issue with spring batch and JPA. My DAO update method is throwing the exception. please help me on this.

                  • #11
                    Thanks Oliver, it works great!!!!

                    I use Set instead of List to avoid the delete"'s

                    Code:
                    	@ManyToMany
                    	private Set<Account> accounts;
                    Last edited by venosov; Mar 13th, 2012, 12:23 PM.

                    Comment

                    Working...
                    X