开发者

Spring @Transactional - using Collections after persist/merge

开发者 https://www.devze.com 2023-01-26 02:59 出处:网络
I am using Spring and JPA in my application, but lately I got a little confused with using collections from DomainObjects.

I am using Spring and JPA in my application, but lately I got a little confused with using collections from DomainObjects.

For Example:

Imagine we have two tables: Parent and Child. A parent can have several Childs (@OneToMany) and a child can only have one parent (@ManyToOne). I use Spring to handle my transaction context in my ServiceLayer (not Dao - Unit of Work pattern), which is configured in context-application.xml by aop:config. Children collections is marked as LazyLoading Collection. Everything worked fine until now.

The problem now is the following:

I need a method in my service, which creates a new Parent and calling another method in same transaction to do some calculations with the newly created Parent. Within these calculations I have to call parent.getChildren(). Obviously this should return an empty Set, since a new Parent cant have any Children at creationtime, but for some reason I get back 'null' instead of an empty set, which results in a NullpointerException

To create a parent I tried the following EntityManager methods:

  1. using entityManager.persist( parent ) - didnt work out, collection is null after creation.
  2. using entityManager.merge( parent ) and return the new managed instance of parent - had the same result.

Both resulted in a null set. But I am pretty sure, that these calls are within the same transaction, cause outside of a transaction I would receive a LazyLoadingExce开发者_如何学编程ption (instead of NullPointer) which is (afaik) correct at this point. On the other hand using an already existing parent with entityManager.findById() resulted in an existing collection (which means it works like expected).To fix the issue I tried several approaches until I found a working one:

entityManager.refresh( parent ) after merging/persisting worked out, since the collections get initialised when calling refresh.

But within tutorials I never saw that this method is beeing used. In JavaDoc of EntityManager its been said, that merge returns the new managed beans, while persist makes the reference itself managed. I guessed managed would mean, that I can already use those Collections within a transaction. Why do the collections not getting initialized at creation time? Do i have to initialize them by myself in constructor of Parent for example?

I wonder, if I did anything wrong, or is it the right way to call refresh() after I created a new Instance of Parent? If so guess I just have to seperate between persist and update like it is recommended in the following article: JPA Implemenentations and refresh the newly created object after persist? Or should I just refresh the Object, when I really need to use the Collection?

Hope you could follow me and I would appreciate any recommendations or solutions for me to get clear(er) on that point.

Thank you very much,

ymene


Do you initialize your collection fields?

@OneToMany
private Set<Foo> foos = new HashSet<Foo>();

as opposed to just

@OneToMany
private Set<Foo> foos;

Because the latter will of course throw NPEs.


Try using CascadeType.ALL in the parent class.

@OneToMany(cascade = CascadeType.ALL)
public List<Child> getChildren() {
  return children;
}

Secondly use OpenSessionInViewFilter. This will ensure that you could call parent.getChildren() even from the views and the hibernate session remains open until the response is sent back. Below is how you would configure it in your web.xml

<filter>
 <filter-name>OpenSessionInViewFilter</filter-name>
 <filter-class>com.isavera.hibernate.OpenSessionFilter</filter-class>
   <init-param>
     <param-name>singleSession</param-name>
 <param-value>false</param-value>
   </init-param>
 </filter>

<filter-mapping>
 <filter-name>OpenSessionInViewFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

Hope this helps

0

精彩评论

暂无评论...
验证码 换一张
取 消