123
返回列表 发新帖
楼主: justforregister

The PathProxy pattern: Persisting complex associations

[复制链接]
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
21#
 楼主| 发表于 2009-3-15 23:53 | 只看该作者
Create a PathProxyThe createPathProxy() method handles the job of generating a PathProxy if one isn't found. Listing 7 looks into that method.
Listing 7. createPathProxy()@Transactionalprotected PathProxy createPathProxy(ManagedObject[] moPath){    if (log.isInfoEnabled()) { log.info("| | | | | BEGIN createPathProxy: " + ArrayUtils.toString(moPath)); }    // Get PP object    PathProxy newPp = new PathProxy();    int childIndex = moPath.length - 1;    newPp.setEntityId(moPath[childIndex].getId());    newPp.setEntityType(moPath[childIndex].getClass().getName());    if (log.isTraceEnabled()) { log.trace("Set id: " + newPp.getEntityId() + " and type: " + newPp.getEntityType() + " on new PathProxy: " + newPp); }    this.getJpaTemplate().persist(newPp);    if (log.isTraceEnabled()) { log.trace("Saved newPp: " + newPp); }    // Here is where we recursively build up the path until we find a parent path proxy that exists    // Remove the current mo from path, to get the parent path    Object[] objParentPath = ArrayUtils.remove(moPath, childIndex);    // Convert back to mo array    ManagedObject[] parentPath = new ManagedObject[objParentPath.length];    for (int i = objParentPath.length - 1; i >= 0; i--){        parentPath = (ManagedObject)objParentPath;    }    // Get parent TreePoint    PathProxy parentPp = (PathProxy)getPathProxyFromMoArray(parentPath, true);    if (log.isTraceEnabled()) { log.trace("Here's the parentPp: " + parentPp); }    // Set the parent    newPp.setParent(parentPp);    // This will either get the existing parentPp with its parents in place, or create a new one and get its parent, and so forth    if (log.isTraceEnabled()) { log.trace("RETURN newPp: " + newPp + " with parent: " + newPp.getParent()); }    return newPp;}

The first thing to notice about Listing 7 is that it's a @Transactional method, because it updates the database when it creates a new PathProxy. The second thing to notice is that the method is recursive. In order to create the PathProxy, we need to get the parent proxy. Therefore, we create a parentPath, and invoke getPathProxyFromMoArray() on it. That will in turn end up at the create method if the parent can't be found. In this way, the algorithm recursively searches back to the earliest ancestor and, starting there, at the beginning, ensures that every node on the path exists. If a node does not exist, it creates it.
The way the algorithm works is important, because it ensures there is one and only one PathProxy for each node.

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
22#
 楼主| 发表于 2009-3-15 23:54 | 只看该作者
Creating path-specific associationsNow that you've seen how retrieving children from a path works, let's use the application to create some children, and see how that is accomplished. Remember that I use the term path-specific to refer to associations that require knowledge of other relationships -- exactly what the PathProxy is for. If you deploy the application and look at the first page, you'll see something similar to the screenshot in Figure 4.

Figure 4. Index.jsp with no dataThe extremely Spartan interface in Figure 4 has just enough to show off PathProxy's stuff. You can see that there are no instances of our domain classes. Creating some of these objects is straightforward, just use the Add links. After you do that, you'll see the tree reflect the new data, as in Figure 5.

Figure 5. Index.jsp with some objects createdNow we can add a manager to a project by clicking on the project. Selecting a manager you've created previously and hitting OK will add it. Remember, that is being done via a JPA many-to-many mapping. Its pretty standard stuff. Once you've done that, however, you will be able to expand the tree down to the manager on the project. Clicking on the manager in the tree allows you to add a developer to the manager -- but only for that project!
How does that work? The first thing to realize is that when you click on the manager under the Managers on Project node of a Project, it actually sends back a nodeString to the server, which is saved in the session for use while the manager is being edited. To see what I mean, look at the SessionBean. Specifically, look at getCurrentSelection() in the (heavily truncated) Listing 8. That method is used by the managerDetail.jsp screen to get the current object, which also causes the method to grab the nodeString from the request parameters and save it. Since this bean is session scoped, we can then grab the nodeString later on and use it when setting a developer on the manager. That is, we'll use the nodeString to actually set the developer, via the pathProxy mechanism, on the path represented by the nodeString.

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
23#
 楼主| 发表于 2009-3-15 23:54 | 只看该作者
Listing 8. getCurrentSelection, truncated to show the essentialspublic Object getCurrentSelection(){        ...        String selectionType = (String)context.getExternalContext().getRequestParameterMap().get("selectionType");        String selectionNodeString = (String)context.getExternalContext().getRequestParameterMap().get("selectionNodeString");        ...        String[] nodeStringSplit = selectionNodeString.split("\\^");        ...                this.selectionNodeString = selectionNodeString;}

When you select a developer on the Manager detail and click OK, what happens? Take a look at Listing 9.
Listing 9. Adding developers to a Project->Manager pathpublic void setDevelopersOnSelectedManager(List devsFromUi){    assert(Manager.class.getName().equals(sessionBean.getSelectionType()));    String nodeString = sessionBean.getSelectionNodeString();    EntityPath path = this.getNewPath().setPathAsString(nodeString);    if (log.isTraceEnabled()) { log.trace("path: " + ArrayUtils.toString(path)); }    List existingO = pathProxyService.getPathChildren(path, Developer.class.getName());    List<Developer> actualDevs = new ArrayList<Developer>();    for (Object o : existingO){        Developer existingDev = (Developer)o;        if (!devsFromUi.contains(existingDev)){            pathProxyService.removeChild(path, existingDev);        }    }    for (Object o : devsFromUi){        Developer newDev = (Developer)o;        if (!existingO.contains(newDev)){            pathProxyService.addChild(path, newDev);        }    }}

As you can see, this method essentially gets the nodeString and, wrapping it in an EntityPath object, uses it with the pathProxyService to get all the developers that already exist on that path. The rest of the method is logic for removing any Developers that do not exist in the list passed back from the UI, adding any that are in that list, but don't exist yet, and leaving everything else the same (i.e., leaving the developers that exist both in the DB and in the UI list).
I think this method is pretty self-explanatory. It relies on the addChild() and removeChild() methods of the PathProxyService. If you look at those methods, you'll see that they are very simple. Their real complexity is all in the getPathProxyFromMoArray() method, which we've already covered!

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
24#
 楼主| 发表于 2009-3-15 23:54 | 只看该作者
In Figure 6 you can see I've recreated the example from the beginning of the article, Figure 1, with Robert, Mukunda and Johnie.

Figure 6. The tree showing different developers on the same manager for different projectsWhat Figure 6 shows is that Johnie, the manager, has got Mukunda on one project and Robert on another. You can also see these relationships illustrated in the PathProxy table.
Table 1. The PathProxy tablepath_proxy_identity_identity_typeparent_id27197484128769tutorial.model.Project(null)27197494096001tutorial.model.Developer271974727197443670016tutorial.model.Manager271974527197454128768tutorial.model.Project(null)27197464096000tutorial.model.Developer271974427197473670016tutorial.model.Manager2719748As you can see, there is one PathProxy for every unique path. Each project, as the root, has a PathProxy, whose parent is null. There are two PathProxy objects pointed to the same manager, each having the appropriate project PathProxy as a parent. Finally, each developer has a PathProxy, with the managers as parents.

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
25#
 楼主| 发表于 2009-3-15 23:55 | 只看该作者
Adding another entityI mentioned at the beginning of this article that PathProxy would make adding another entity to our system easy. To prove it, I added a Task entity and its relationship to Developer to the system in just a couple of hours. The PathProxy really helped on the back end, because I had only one class and one table to add.
Besides that, all the work is in the UI: addTask.jsp, developerDetail.jsp, adding a facet to the index.jsp tree, and backing bean stuff: adding the TaskBean, adding a couple methods to PersonBean, and adding the node handling for Task to AjaxTreeBean.
The PathProxy itself did not change at all. The UI and backing bean code would have to happen regardless of how the relationship were maintained. It is simpler with the PathProxy in place, however. You don't have to add a class and table for the relationship, and there's less churn in the changes made to the backing bean code because it relies on the same PathProxy logic, and doesn't have to deal with a new class.
Seeing everything that doesn't have to be done to add in the Task class is great confirmation that the PathProxy pattern keeps the domain model and persistence layer stable -- what a relief!
In conclusionThe key benefits to the PathProxy pattern are consistency and extensibility. PathProxy's enforced consistency greatly simplifies the overall system design. Its extensibility makes a routine of adding more relationships, even ones with extremely complicated pathing. Just build the path and hand it off to the PathProxy. You can do that as many times as you need to without changing the database schema or adding any more classes.
Like most design patterns, the PathProxy really needs to be justified in its usage, otherwise you risk over-complexity. Also like other design patterns, if your situation calls for it, it is exactly what you need and can provide a major foundation to your system, becoming an architectural principle as well as a design pattern.
I hope you will use the PathProxy pattern for those situations where it is just what you need.

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
26#
 楼主| 发表于 2009-3-15 23:55 | 只看该作者
Adding another entityI mentioned at the beginning of this article that PathProxy would make adding another entity to our system easy. To prove it, I added a Task entity and its relationship to Developer to the system in just a couple of hours. The PathProxy really helped on the back end, because I had only one class and one table to add.
Besides that, all the work is in the UI: addTask.jsp, developerDetail.jsp, adding a facet to the index.jsp tree, and backing bean stuff: adding the TaskBean, adding a couple methods to PersonBean, and adding the node handling for Task to AjaxTreeBean.
The PathProxy itself did not change at all. The UI and backing bean code would have to happen regardless of how the relationship were maintained. It is simpler with the PathProxy in place, however. You don't have to add a class and table for the relationship, and there's less churn in the changes made to the backing bean code because it relies on the same PathProxy logic, and doesn't have to deal with a new class.
Seeing everything that doesn't have to be done to add in the Task class is great confirmation that the PathProxy pattern keeps the domain model and persistence layer stable -- what a relief!
In conclusionThe key benefits to the PathProxy pattern are consistency and extensibility. PathProxy's enforced consistency greatly simplifies the overall system design. Its extensibility makes a routine of adding more relationships, even ones with extremely complicated pathing. Just build the path and hand it off to the PathProxy. You can do that as many times as you need to without changing the database schema or adding any more classes.
Like most design patterns, the PathProxy really needs to be justified in its usage, otherwise you risk over-complexity. Also like other design patterns, if your situation calls for it, it is exactly what you need and can provide a major foundation to your system, becoming an architectural principle as well as a design pattern.
I hope you will use the PathProxy pattern for those situations where it is just what you need.

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表