Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

The PathProxy pattern: Persisting complex associations

Illustrated With JPA, Spring, and JSF

  • Print
  • Feedback

Page 4 of 7

Getting children on a path

In the case of Managers, Developers, and Projects, the logic is simple: grab all the instances of each type and wrap them in AjaxTreeNodes. Things get more interesting when we start dealing with the pathing. We want to show the managers who are on the project, and also the developers working for each manager on each project. You can see this in Listing 4.

Listing 4. AjaxTreeBean.getChildNodes() - part 2

// ... Continued from Listing 3
  } else if (path.getSize(true) > 0){

            Project clickedProject = (Project)path.getEntity(0); // Get the project, which is the first object in the path

            // In this system, only a project instance is alone in a path
            if (path.getSize(true) == 1) { // A project instance
                if (path.getPathAsString().indexOf("managersOnProject") == -1) {
                    if (log.isTraceEnabled()) { log.trace("clickedProject: " + clickedProject); }
                    int manOnProjectCount = clickedProject.getManagers().size();
                    if (log.isTraceEnabled()) { log.trace("manOnProject: " + manOnProjectCount); }

                    PathHeader header = new PathHeader("Managers On Project");

                    children.add(new AjaxTreeNode(nodeString + EntityPath.NODE_STRING_SEPARATOR + EntityPath.getObjectAsNodeString(header),
                            manOnProjectCount <= 0, header.getLabel()));
                }
            } else if (path.getSize(true) == 2) {
                if (log.isTraceEnabled()) { log.trace("Opening managersOnProject node."); }
                for (Manager manager : clickedProject.getManagers()){
                    String manOnProjectNodeString = nodeString + EntityPath.NODE_STRING_SEPARATOR + EntityPath.getObjectAsNodeString(manager);
                    // Notice we just add the new child node (manager) to the path to get the child developers, to determine the leafs status of the node
                    List<Object> projectManagerDev = pathProxyService.getPathChildren(path.addChild(manager), Developer.class.getName());
                    children.add(new AjaxTreeNode(manOnProjectNodeString, projectManagerDev.size() <= 0,
                            manager.getFirstName(), Manager.class.getName(), manager));
                }

            } else if (path.getSize(true) == 3) { // project->header->manager
                List<Object> projectManagerDev = pathProxyService.getPathChildren(path, Developer.class.getName());
                for (Object dev : projectManagerDev){
                    Developer developer = (Developer)dev;
                    String devNodeString = nodeString + EntityPath.NODE_STRING_SEPARATOR + EntityPath.getObjectAsNodeString(developer);
                    List<Object> projectManagerDevTask = pathProxyService.getPathChildren(path.addChild(developer), Task.class.getName());
                    children.add(new AjaxTreeNode(devNodeString, projectManagerDevTask.size() <= 0,
                            developer.getFirstName(), Developer.class.getName(), developer));
                }
            } else if (path.getSize(true) == 4){ // Again, we know a three node path is project->manager->developer because the system is so simple
                List<Object> tasksOnDev = pathProxyService.getPathChildren(path, Task.class.getName());
                children.addAll(this.getMoNodeList(tasksOnDev));
            }
        } else {
            throw new IllegalArgumentException("Unknown node type: " + nodeString);
        }
        if (log.isInfoEnabled()) { log.info("RETURNING children: " + children); }
        return children;
    }


Listing 4 continues the handling of the nodeStrings. It operates by checking how many nodes are in the nodeString. This is a very simple strategy for figuring out what node has been clicked on. In a more complex system, you'd need more complex rules. This method is very clean and neat; the challenge is to keep the logic that way as your relationships grow more complex and involved. A couple of tips:

  • Use the nodeString to keep your logic neat, you can define it however you want.
  • Push complexity into the EntityPath class. The way I'm doing it here is a great template, but I encourage you to find strategies that suit your situation.

After determining what node has been clicked on, the method delegates to the service layer to get the children. You'll notice that when we need to get the developers that are assigned to a manager on a particular project, we make a call to PathProxyService. We'll take a look at that class in just a moment.

The EntityPath class

You might also have noticed the class EntityPath in Listing 4. This class basically encapsulates a path in the system, which has a number of advantages:

  • It holds all the logic for manipulating paths (like adding a child in Listing 4).
  • It allows you to pass the object around the layers of your app without leaking any information.
  • In a large system, you would probably want to hide all information regarding the string representation of paths inside the EntityPath.
  • Anytime you want a string path, you get it from the EntityPath.

Note that the EntityPath implementation here does not do everything listed above, but it does show you the role it can play in your persistence architecture.

A quick note about how we're getting instances of EntityPath in Listing 4. This technique is called Lookup method injection in Spring terminology (check out section 3.3.4.1 of the Spring docs). It's a little funky, but it suits our purposes here.

  • Print
  • Feedback

Resources
  • Download the sample application for this article, built with Spring, JPA/Hibernate, and JSF. The application build tool was Maven 2 and it was tested on Tomcat 6.
  • PathProxy isn't Matt Tyson's first published design pattern. Learn about his design strategies for Ajax-style development in JavaServer Faces: the AjaxCommand strategy and the AjaxComponent strategy, both published by JavaWorld.
  • The Spring Framework Documentation Section 3.3.4.1 explains Spring's lookup method injection.
  • See the JPOX homepage for a good discussion of JPA inheritance.
  • "Understanding the Java Persistence API" (Aditi Das, JavaWorld, January 2008) is a two-part introduction to Java-platform persistence with JPA. Part 2 walks through an example implementation of JPA annotations.
  • "J2EE design decisions" (Chris Richardson, JavaWorld, January 2006) explores five design patterns for implementing enterprise applications using lightweight frameworks such as Spring and Hibernate. An excerp from POJOs in Action; Manning Publications, January 2006.
  • "Get started with Hibernate" (Christian Bauer and Gavin King, JavaWorld, October 2004) is a short introduction to Hibernate written by its creator, Gavin King. Excerpted from Hibernate in Action; Manning 2004.)
  • Visit the JavaWorld Java Standard Edition research center for more articles about core Java programming tools, design patterns, and concepts.
  • Also see Network World's IT Buyer's Guides: Side-by-side comparison of hundreds of products in over 70 categories.