[jboss-user] [EJB 3.0] - Re: LazyInitializationException in many-to-many relation tha

JuliaF do-not-reply at jboss.com
Sun Apr 27 09:38:05 EDT 2008


Hello,  jaikiran

Many thanks for posting the tip. I fixed the LazyInitialisationException problem in this instance. However, I think I am facing a much more general EJB design problem, which I will try to explain further down.
With regards to the earlier posting, in addition to using merge() before persist(), as you helpfully suggested, I also had to add this:
@TransactionAttribute(TransactionAttributeType.REQUIRED) 
  | public void addPublicSubject(Subject s, User u) { 
  | 
  |         User u = entityManager.merge(user);
  | 	Subject s = entityManager.merge(subject);
  |   	
  |         u.getSubjectsThisUserSubscribedTo().add(s);
  | 	s.getUsersThatSubscribedToMe().add(u);
  |   			
  | 	entityManager.persist(u);
  | 	entityManager.persist(s);
  | 
  | }
  | 
Thus, it updates the relationship table (shared_subject in this case)

In addition I modified findSubject(int subjectId) of session bean SubjectBean as follows

  | @TransactionAttribute(TransactionAttributeType.REQUIRED)
  | public Subject findSubject(int subjectId) {
  | 	Subject s = (Subject)entityManager.find(Subject.class, subjectId);
  | 	
  |         s.getOwner();
  | 	s.getUpdater();
  | 	Collection<Topic> temp_topics= s.getMyTopics();
  | 	if(!temp_topics.isEmpty()) temp_topics.size();
  | 	Set<User> temp_users = s.getUsersThatSubscribedToMe();
  | 	if(!temp_users.isEmpty()) temp_users.size();
  | 	return s;
  | }
  | 
because I read in one of the tutorials that all relationships must be managed explicitly in the transactional code. 
That is if we want the client to have access to the cmr fields of the Subject they must be pre-fetched within the transaction by executing seemingly useless statements like collectionXXX.size() or s.getOwner() even though we don™t assign the result of such statements to anything and don™t use it within the transaction. 

EJB design problem:
So, to avoid LasyInitialisationException CMR Fields first have to be accessed in the transactional context of a session bean in order to be loaded into the Entity Bean 
Suppose, I have a bean User with the following CMR fields:


  |         Set<Subject> mySubjects;
  | 	Set<Topic> topicsThisUserCreated;
  | 	Set<Entry> entriesThisUserAuthored;
  | 	Set<Topic> topicsThisUserUpdated;
  | 	Set<Topic> entriesThisUserUpdated;
  | 	Set<Subject> subjectsThisUserSubscribedTo;
  | 
Respectively each object in each collection has CMR fields

  | 
  | Subject:
  |        User owner;
  | 	User updater;
  | 	Collection<Topic> myTopics;
  | 	Set<User> usersThatSubscribedToMe;
  | Topic:
  | 	User creator;
  | 	User updater;
  | 	Subject subject;
  | 	Topic parentTopic;
  | 	private Collection<Topic> nestedTopics;
  | 	private Set<Entry> entries;
  | 
  | Entry:
  | 	private Topic topic;
  | 	private User author;
  | 	private User updater;
  |                 Plus entry has a field:
  |         private String entryBody; which is @Lob
  | 

When User is accessed in the detached context (somewhere in the client) technically speaking it is possible that the client will call something like:

  | public void buildOutput(User user, Output out){
  |       User u = user;
  |       Set<Subject> ss = u.getSubjectsUserSubscribedTo();
  |       for(subject:ss) {
  |       Set<Topic> tt = subject.getMyTopics();
  |       for(topic:tt) {
  | 		outputTopic(topic, u, out);
  | 		
  | 	}
  | }
  | public void outputTopic(Topic topic, User u, out) {
  | 
  |        if(topic.getParent()!=null) outputTopic(topic.getParent(), u, out);
  |        out.setElemet(Topic.class.getName());
  | 	out.add(topic.getName);
  | 	out.add(topic.getCreator().getUserName());
  | 	out.add(topic.getUpdater().getUserName();
  |         out.add(topic.getCreator().getTopicsThisUserUpdated().size());
  |         Set<Entry> ee = topic.getEntries();
  |         for(entry:ee){
  | 	         out.setElement(Entry.class.getName());
  | 	         out.add(entry.getName();
  |                  out.add(entry.getAuthor().getUserName());
  | 	         out.add(entry.getBody());
  |         }
  | }
  | 
  | 
I don™t think it will work because LazyInitializationException can be thrown in the dozens of places in this snippet of code. 
This poses a design issue: 
The getters and setters of CMR fields must be public so that the EJB container could manage them.
If they are public the client has access to them in the detached context, but to avoid Lazy Loading Exception they have to be set and accessed in the transactional context of a session bean.
My understanding is that EJB3 disposed of the Data Transfer and Data Access objects in order to simplify the EJB model and instead introduced the notion of the detached context in which Data Access objects (User, Subject, Topic, Entry in this case) act as Data Transfer Objects as far as client is concerned. However, LazyInitializationException will always be on the way in this model.
So, the question is how can we ensure that the client doesn™t access CMR fields outside of transactional context from the design point of view?


View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4147060#4147060

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4147060




More information about the jboss-user mailing list