Transfer (Value) objects, CMR, Collections, and Recursion

Discussions

EJB programming & troubleshooting: Transfer (Value) objects, CMR, Collections, and Recursion

  1. Hi TSS,

    I have a bunch of EJB 2.0 Entity beans which have CMR navigable relationships between them, I want to return transfer objects (getData() method). My problem is, what do I return for the relationships. It's easy if the relationship method is on the one side of a 1-*, I just return a invoke the getData() method of that entity bean. But on the many side of the relationship, if I iterate through the collection, I will eventually invoke the getData() method of the other entity bean so I will have an infinite recursion.

    I'm sure someone has run into this and has a good pattern.

    Thanks,

    Gordon
  2. I generally avoid these kind of relationship recursion problems by lazy loading the related values. In your case, the logic would have to go in the Data Transfer Objects (DTO) themselves.

    For example, suppose you have DTO1, EJB1 and DTO2, EJB2 in a many-to-many relationship. You might have logic in DTO1 like the following to load the related DTO2:

    public class DTO1 ...
      private Long id; // The PK fot EJB1
      private Collection dto2List;

      ...

      public Collection getDTO2() {
        if (this.dto2List == null) {
          this.dto2List = new LinkedList();
          EJB2Home ejb2Home = // ... load EJB2Home
          Collection relations = ejb2Home.findRelatedToEJB1(this.id);
          while(relations.hasNext() {
            EJB2Remote ejb2 = (EJB2Remote) relations.next();
            DTO2 dto2 = ejb2.getData();
            this.dto2List.add(dto2);
          }
        }
        return this.dto2List;
      }
    }

    You can define symmetrical lazy-loading methods in DTO2. The problem with this approach is that if you pass the DTO all the way up to the client layer, you will end up with additional server calls to lazy-load data as you walk through the object hierarchy. You can avoid this problem by writing a session EJB that pre-populates the appropriate subset of your object model:

    public class PrePopSessionEJB ...
      public DTO1 getPrePopulatedDTO1(Long id) {
        EJB1Home ejb1Home = // ... load EJB1Home
        DTO1 dto1 = ejb1Home.findByPrimaryKey(id).getData();
        dto1.getDTO2(); // Load DTO2 data
        return dto1;
      }

      public DTO2 getPrePopulatedDTO2(Long id) {
        EJB2Home ejb2Home = // ... load EJB2Home
        DTO2 dto2 = ejb2Home.findByPrimaryKey(id).getData();
        dto2.getDTO1(); // Load DTO1 data
        return dto2;
      }
    }

    All of this is pretty elaborate, I admit. Personally, I prefer to write applications that use simpler persistence technology like Hibernate and JDO, where these things tend to be non-issues. If you do go this route, I strongly suggest you try to generate all this code rather than cranking it out by hand.