楼主: 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
11#
 楼主| 发表于 2009-3-15 23:50 | 只看该作者
The EntityPath classYou 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.

The PathProxyService interfacePathProxyService is the interface implemented by our PathProxyDao (this simple application doesn't really require a well-defined service layer). Listing 5 displays the PathProxyDao method we use to get children from a path.
Listing 5. getPathChildren()public List<Object> getPathChildren(EntityPath path, String type){    PathProxy parentPp = this.getPathProxyFromMoArray(path.getPathAsObjects(), false);    if (log.isTraceEnabled()) { log.trace("parentPp: " + parentPp); }    List<Object> list = new ArrayList<Object>();    if (parentPp == null){        if (log.isInfoEnabled()) { log.info("No object found on path: " + ArrayUtils.toString(path)); }    } else {        String hql = "select t from " + type + " t, PathProxy pp where t.id = pp.entityId " +            " and pp.parent = arentPp";        if (log.isTraceEnabled()) { log.trace("hql: " + hql); }        Map<String, Object> params = new HashMap<String, Object>();        params.put("parentPp", parentPp);        list = this.getJpaTemplate().findByNamedParams(hql, params);    }    if (log.isInfoEnabled()) { log.info("RETURN list: " + list); }    return list;}

You can see this method is actually pretty simple. That's because most of the complexity is in the getPathProxyFromMoArray() method. Let's dive right into that, because it is the heart and soul of the PathProxy pattern's magic.

使用道具 举报

回复
论坛徽章:
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
12#
 楼主| 发表于 2009-3-15 23:50 | 只看该作者
The EntityPath classYou 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.

The PathProxyService interfacePathProxyService is the interface implemented by our PathProxyDao (this simple application doesn't really require a well-defined service layer). Listing 5 displays the PathProxyDao method we use to get children from a path.
Listing 5. getPathChildren()public List<Object> getPathChildren(EntityPath path, String type){    PathProxy parentPp = this.getPathProxyFromMoArray(path.getPathAsObjects(), false);    if (log.isTraceEnabled()) { log.trace("parentPp: " + parentPp); }    List<Object> list = new ArrayList<Object>();    if (parentPp == null){        if (log.isInfoEnabled()) { log.info("No object found on path: " + ArrayUtils.toString(path)); }    } else {        String hql = "select t from " + type + " t, PathProxy pp where t.id = pp.entityId " +            " and pp.parent = arentPp";        if (log.isTraceEnabled()) { log.trace("hql: " + hql); }        Map<String, Object> params = new HashMap<String, Object>();        params.put("parentPp", parentPp);        list = this.getJpaTemplate().findByNamedParams(hql, params);    }    if (log.isInfoEnabled()) { log.info("RETURN list: " + list); }    return list;}

You can see this method is actually pretty simple. That's because most of the complexity is in the getPathProxyFromMoArray() method. Let's dive right into that, because it is the heart and soul of the PathProxy pattern's magic.

使用道具 举报

回复
论坛徽章:
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
13#
 楼主| 发表于 2009-3-15 23:50 | 只看该作者
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.

The PathProxyService interface

PathProxyService is the interface implemented by our PathProxyDao (this simple application doesn't really require a well-defined service layer). Listing 5 displays the PathProxyDao method we use to get children from a path.
Listing 5. getPathChildren()

public List<Object> getPathChildren(EntityPath path, String type){
    PathProxy parentPp = this.getPathProxyFromMoArray(path.getPathAsObjects(), false);
    if (log.isTraceEnabled()) { log.trace("parentPp: " + parentPp); }
    List<Object> list = new ArrayList<Object>();
    if (parentPp == null){
        if (log.isInfoEnabled()) { log.info("No object found on path: " + ArrayUtils.toString(path)); }
    } else {
        String hql = "select t from " + type + " t, PathProxy pp where t.id = pp.entityId " +
            " and pp.parent = arentPp";
        if (log.isTraceEnabled()) { log.trace("hql: " + hql); }
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("parentPp", parentPp);
        list = this.getJpaTemplate().findByNamedParams(hql, params);
    }
    if (log.isInfoEnabled()) { log.info("RETURN list: " + list); }
    return list;
}


You can see this method is actually pretty simple. That's because most of the complexity is in the getPathProxyFromMoArray() method. Let's dive right into that, because it is the heart and soul of the PathProxy pattern's magic.

使用道具 举报

回复
论坛徽章:
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
14#
 楼主| 发表于 2009-3-15 23:51 | 只看该作者
The EntityPath classYou 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.

The PathProxyService interfacePathProxyService is the interface implemented by our PathProxyDao (this simple application doesn't really require a well-defined service layer). Listing 5 displays the PathProxyDao method we use to get children from a path.
Listing 5. getPathChildren()public List<Object> getPathChildren(EntityPath path, String type){    PathProxy parentPp = this.getPathProxyFromMoArray(path.getPathAsObjects(), false);    if (log.isTraceEnabled()) { log.trace("parentPp: " + parentPp); }    List<Object> list = new ArrayList<Object>();    if (parentPp == null){        if (log.isInfoEnabled()) { log.info("No object found on path: " + ArrayUtils.toString(path)); }    } else {        String hql = "select t from " + type + " t, PathProxy pp where t.id = pp.entityId " +            " and pp.parent = arentPp";        if (log.isTraceEnabled()) { log.trace("hql: " + hql); }        Map<String, Object> params = new HashMap<String, Object>();        params.put("parentPp", parentPp);        list = this.getJpaTemplate().findByNamedParams(hql, params);    }    if (log.isInfoEnabled()) { log.info("RETURN list: " + list); }    return list;}

You can see this method is actually pretty simple. That's because most of the complexity is in the getPathProxyFromMoArray() method. Let's dive right into that, because it is the heart and soul of the PathProxy pattern's magic.

使用道具 举报

回复
论坛徽章:
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
15#
 楼主| 发表于 2009-3-15 23:51 | 只看该作者
The heart of the PathProxy patterngetPathProxyFromMoArray() is a fairly dense method. I'll list it all at once here and then refer back to it.
Listing 6. getPathProxyFromMoArray()public PathProxy getPathProxyFromMoArray(ManagedObject[] moArray, boolean createIfNotFound){    if (log.isInfoEnabled()) { log.info("= = = = = = =BEGIN getPathProxyFromVoArray(): " + ArrayUtils.toString(moArray)); }    if (moArray == null || ArrayUtils.isEmpty(moArray)){        if (log.isInfoEnabled()) { log.info("Got an empty voArray, returning null."); }        return null;    }    // Build a query string that has as many name/id parameters as there are vo in the array    // Note, this may not work with entities using composite keys - hibernate issues invalid sql for null    StringBuffer queryString = new StringBuffer("select pp from PathProxy pp "        + "where "); // (A)    if (moArray.length > 0) {        for (int i = moArray.length - 1; i >= 0; i--){ // (B)            if (log.isTraceEnabled()) { log.trace("Creating ppClause for VO: " + i + " : " + moArray); }            StringBuffer ppClause = new StringBuffer("pp"); // (C)            // Add as many .parent's to the pp as we have counted into the hierarchy, this gets us the parent pp (eg, pp.parent.parent when i = 2)            for (int parentCount = moArray.length - 2; parentCount >= i; parentCount--){ // (D)                ppClause.append(".parent");            }            // (E)            if ((moArray != null) && (!moArray.equals("null"))){ // If not null, add type and id                String ppEntityType = ppClause.toString() + "." + PathProxy.ENTITY_TYPE_PROPERTY + " = '" + moArray.getClass().getName() + "'";                // Here's where we benefit from using the ManagedObject base class ... no need to figure out what property to use for the ID                String ppEntityId = ppClause.toString() + "." + PathProxy.ENTITY_ID_PROPERTY + " = " + moArray.getId();                queryString.append(ppEntityId);                queryString.append(" and ");                queryString.append(ppEntityType);                if (i > 0){ // Add an 'and' if we will add more ppClauses to the queryString                    queryString.append(" and "); // (F)                }            // (G)            } else { // This vo is null, so add tp.parent = null                // This is where Hibernate generates bad sql for composite keys, removing fixes that:                queryString.append(ppClause.toString() + " is null");                // Remove and added by last iteration (if you disable the 'is null')//                    queryString.delete(queryString.length() - 4, queryString.length() ); //(" and ")            }        }    } else {        // Shouldn't get here        return null;    }    if (log.isTraceEnabled()) { log.trace("------ Here's the queryString: " + queryString + " ------"); }    List lst = this.getJpaTemplate().find(queryString.toString());    if (log.isTraceEnabled()) { log.trace("Query for path proxy with moArray returned: " + lst); }    PathProxy ppToReturn = null;    if (CollectionUtils.isEmpty(lst)) { // No path proxy there yet        if (createIfNotFound){            if (log.isInfoEnabled()) { log.info("No pathproxy on that path, creating new..."); }            ppToReturn = createPathProxy(moArray);        } else {            ppToReturn = null;        }    } else {        ppToReturn = (PathProxy)lst.get(0);    }    if (log.isTraceEnabled()) { log.trace("RETURN ppToReturn: " + ppToReturn); }    return ppToReturn;}

使用道具 举报

回复
论坛徽章:
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
16#
 楼主| 发表于 2009-3-15 23:52 | 只看该作者
The heart of the PathProxy patterngetPathProxyFromMoArray() is a fairly dense method. I'll list it all at once here and then refer back to it.
Listing 6. getPathProxyFromMoArray()public PathProxy getPathProxyFromMoArray(ManagedObject[] moArray, boolean createIfNotFound){    if (log.isInfoEnabled()) { log.info("= = = = = = =BEGIN getPathProxyFromVoArray(): " + ArrayUtils.toString(moArray)); }    if (moArray == null || ArrayUtils.isEmpty(moArray)){        if (log.isInfoEnabled()) { log.info("Got an empty voArray, returning null."); }        return null;    }    // Build a query string that has as many name/id parameters as there are vo in the array    // Note, this may not work with entities using composite keys - hibernate issues invalid sql for null    StringBuffer queryString = new StringBuffer("select pp from PathProxy pp "        + "where "); // (A)    if (moArray.length > 0) {        for (int i = moArray.length - 1; i >= 0; i--){ // (B)            if (log.isTraceEnabled()) { log.trace("Creating ppClause for VO: " + i + " : " + moArray); }            StringBuffer ppClause = new StringBuffer("pp"); // (C)            // Add as many .parent's to the pp as we have counted into the hierarchy, this gets us the parent pp (eg, pp.parent.parent when i = 2)            for (int parentCount = moArray.length - 2; parentCount >= i; parentCount--){ // (D)                ppClause.append(".parent");            }            // (E)            if ((moArray != null) && (!moArray.equals("null"))){ // If not null, add type and id                String ppEntityType = ppClause.toString() + "." + PathProxy.ENTITY_TYPE_PROPERTY + " = '" + moArray.getClass().getName() + "'";                // Here's where we benefit from using the ManagedObject base class ... no need to figure out what property to use for the ID                String ppEntityId = ppClause.toString() + "." + PathProxy.ENTITY_ID_PROPERTY + " = " + moArray.getId();                queryString.append(ppEntityId);                queryString.append(" and ");                queryString.append(ppEntityType);                if (i > 0){ // Add an 'and' if we will add more ppClauses to the queryString                    queryString.append(" and "); // (F)                }            // (G)            } else { // This vo is null, so add tp.parent = null                // This is where Hibernate generates bad sql for composite keys, removing fixes that:                queryString.append(ppClause.toString() + " is null");                // Remove and added by last iteration (if you disable the 'is null')//                    queryString.delete(queryString.length() - 4, queryString.length() ); //(" and ")            }        }    } else {        // Shouldn't get here        return null;    }    if (log.isTraceEnabled()) { log.trace("------ Here's the queryString: " + queryString + " ------"); }    List lst = this.getJpaTemplate().find(queryString.toString());    if (log.isTraceEnabled()) { log.trace("Query for path proxy with moArray returned: " + lst); }    PathProxy ppToReturn = null;    if (CollectionUtils.isEmpty(lst)) { // No path proxy there yet        if (createIfNotFound){            if (log.isInfoEnabled()) { log.info("No pathproxy on that path, creating new..."); }            ppToReturn = createPathProxy(moArray);        } else {            ppToReturn = null;        }    } else {        ppToReturn = (PathProxy)lst.get(0);    }    if (log.isTraceEnabled()) { log.trace("RETURN ppToReturn: " + ppToReturn); }    return ppToReturn;}

使用道具 举报

回复
论坛徽章:
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
17#
 楼主| 发表于 2009-3-15 23:52 | 只看该作者
The heart of the PathProxy pattern

getPathProxyFromMoArray() is a fairly dense method. I'll list it all at once here and then refer back to it.
Listing 6. getPathProxyFromMoArray()

public PathProxy getPathProxyFromMoArray(ManagedObject[] moArray, boolean createIfNotFound){
    if (log.isInfoEnabled()) { log.info("= = = = = = =BEGIN getPathProxyFromVoArray(): " + ArrayUtils.toString(moArray)); }

    if (moArray == null || ArrayUtils.isEmpty(moArray)){
        if (log.isInfoEnabled()) { log.info("Got an empty voArray, returning null."); }
        return null;
    }

    // Build a query string that has as many name/id parameters as there are vo in the array
    // Note, this may not work with entities using composite keys - hibernate issues invalid sql for null
    StringBuffer queryString = new StringBuffer("select pp from PathProxy pp "
        + "where "); // (A)

    if (moArray.length > 0) {
        for (int i = moArray.length - 1; i >= 0; i--){ // (B)
            if (log.isTraceEnabled()) { log.trace("Creating ppClause for VO: " + i + " : " + moArray); }

            StringBuffer ppClause = new StringBuffer("pp"); // (C)
            // Add as many .parent's to the pp as we have counted into the hierarchy, this gets us the parent pp (eg, pp.parent.parent when i = 2)
            for (int parentCount = moArray.length - 2; parentCount >= i; parentCount--){ // (D)
                ppClause.append(".parent");
            }
            // (E)
            if ((moArray != null) && (!moArray.equals("null"))){ // If not null, add type and id
                String ppEntityType = ppClause.toString() + "." + PathProxy.ENTITY_TYPE_PROPERTY + " = '" + moArray.getClass().getName() + "'";
                // Here's where we benefit from using the ManagedObject base class ... no need to figure out what property to use for the ID
                String ppEntityId = ppClause.toString() + "." + PathProxy.ENTITY_ID_PROPERTY + " = " + moArray.getId();

                queryString.append(ppEntityId);
                queryString.append(" and ");
                queryString.append(ppEntityType);

                if (i > 0){ // Add an 'and' if we will add more ppClauses to the queryString
                    queryString.append(" and "); // (F)
                }
            // (G)
            } else { // This vo is null, so add tp.parent = null
                // This is where Hibernate generates bad sql for composite keys, removing fixes that:
                queryString.append(ppClause.toString() + " is null");
                // Remove and added by last iteration (if you disable the 'is null')
//                    queryString.delete(queryString.length() - 4, queryString.length() ); //(" and ")
            }
        }

    } else {
        // Shouldn't get here
        return null;
    }

    if (log.isTraceEnabled()) { log.trace("------ Here's the queryString: " + queryString + " ------"); }

    List lst = this.getJpaTemplate().find(queryString.toString());
    if (log.isTraceEnabled()) { log.trace("Query for path proxy with moArray returned: " + lst); }

    PathProxy ppToReturn = null;

    if (CollectionUtils.isEmpty(lst)) { // No path proxy there yet
        if (createIfNotFound){
            if (log.isInfoEnabled()) { log.info("No pathproxy on that path, creating new..."); }
            ppToReturn = createPathProxy(moArray);
        } else {
            ppToReturn = null;
        }
    } else {
        ppToReturn = (PathProxy)lst.get(0);
    }
    if (log.isTraceEnabled()) { log.trace("RETURN ppToReturn: " + ppToReturn); }
    return ppToReturn;
}

使用道具 举报

回复
论坛徽章:
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
18#
 楼主| 发表于 2009-3-15 23:52 | 只看该作者
The heart of the PathProxy pattern

getPathProxyFromMoArray() is a fairly dense method. I'll list it all at once here and then refer back to it.
Listing 6. getPathProxyFromMoArray()

public PathProxy getPathProxyFromMoArray(ManagedObject[] moArray, boolean createIfNotFound){
    if (log.isInfoEnabled()) { log.info("= = = = = = =BEGIN getPathProxyFromVoArray(): " + ArrayUtils.toString(moArray)); }

    if (moArray == null || ArrayUtils.isEmpty(moArray)){
        if (log.isInfoEnabled()) { log.info("Got an empty voArray, returning null."); }
        return null;
    }

    // Build a query string that has as many name/id parameters as there are vo in the array
    // Note, this may not work with entities using composite keys - hibernate issues invalid sql for null
    StringBuffer queryString = new StringBuffer("select pp from PathProxy pp "
        + "where "); // (A)

    if (moArray.length > 0) {
        for (int i = moArray.length - 1; i >= 0; i--){ // (B)
            if (log.isTraceEnabled()) { log.trace("Creating ppClause for VO: " + i + " : " + moArray); }

            StringBuffer ppClause = new StringBuffer("pp"); // (C)
            // Add as many .parent's to the pp as we have counted into the hierarchy, this gets us the parent pp (eg, pp.parent.parent when i = 2)
            for (int parentCount = moArray.length - 2; parentCount >= i; parentCount--){ // (D)
                ppClause.append(".parent");
            }
            // (E)
            if ((moArray != null) && (!moArray.equals("null"))){ // If not null, add type and id
                String ppEntityType = ppClause.toString() + "." + PathProxy.ENTITY_TYPE_PROPERTY + " = '" + moArray.getClass().getName() + "'";
                // Here's where we benefit from using the ManagedObject base class ... no need to figure out what property to use for the ID
                String ppEntityId = ppClause.toString() + "." + PathProxy.ENTITY_ID_PROPERTY + " = " + moArray.getId();

                queryString.append(ppEntityId);
                queryString.append(" and ");
                queryString.append(ppEntityType);

                if (i > 0){ // Add an 'and' if we will add more ppClauses to the queryString
                    queryString.append(" and "); // (F)
                }
            // (G)
            } else { // This vo is null, so add tp.parent = null
                // This is where Hibernate generates bad sql for composite keys, removing fixes that:
                queryString.append(ppClause.toString() + " is null");
                // Remove and added by last iteration (if you disable the 'is null')
//                    queryString.delete(queryString.length() - 4, queryString.length() ); //(" and ")
            }
        }

    } else {
        // Shouldn't get here
        return null;
    }

    if (log.isTraceEnabled()) { log.trace("------ Here's the queryString: " + queryString + " ------"); }

    List lst = this.getJpaTemplate().find(queryString.toString());
    if (log.isTraceEnabled()) { log.trace("Query for path proxy with moArray returned: " + lst); }

    PathProxy ppToReturn = null;

    if (CollectionUtils.isEmpty(lst)) { // No path proxy there yet
        if (createIfNotFound){
            if (log.isInfoEnabled()) { log.info("No pathproxy on that path, creating new..."); }
            ppToReturn = createPathProxy(moArray);
        } else {
            ppToReturn = null;
        }
    } else {
        ppToReturn = (PathProxy)lst.get(0);
    }
    if (log.isTraceEnabled()) { log.trace("RETURN ppToReturn: " + ppToReturn); }
    return ppToReturn;
}

使用道具 举报

回复
论坛徽章:
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
19#
 楼主| 发表于 2009-3-15 23:53 | 只看该作者
First off, "Mo" stands for ManagedObject. So what we are saying with the call to getPathProxyFromMoArray() is, "Give me an array of ManagedObjects representing a path, and I'll give you back the PathProxy -- the one and only unique PathProxy -- which points to that node. This last point is important. There is at most one PathProxy for every unique path.
Notice that getPathProxyFromMoArray() also has a boolean switch to determine if a PathProxy should be generated if one is not found: createIfNotFound. For some operations it makes sense to create the PathProxy, for example, when you are adding a child to the path. If you are deleting or checking for the existence of children, however, it does not make sense!
The core idea behind finding a PathProxy is building an HQL query (actually, a JPA-QL query) that will return us the unique PathProxy for the Object array. We do this by saying, "select from pathProxy pp where pp.entityType = ? and pp.entityId = ? and pp.parent.entityType = ? and pp.parent.entityId = ? ...". Essentially, as long as there are more parents in the array, we slap on another .parent.
Footnotes to Listing 6I've added some footnotes to Listing 6, I'll quickly go over them here.

A: this line sets up the beginning of the Query. B: counts backward from the moArray because our path goes from parent-->child. This is just a decision. You could go from child-->parent.
C: begins a StringBuffer that will hold our "PathProxy clause".
D: adds on as many ".parents" as needed. You'll notice that the first time through, there is no ".parent" added -- that's because we want the first PathProxy to point directly to the first child. Basically, this inner for loop counts to 1 less than the number of path objects the outer loop has traversed , adding a .parent each time.
So (not to belabor the point but), the first time through there are no .parents added. The fifth time, there would be four: pp.parent.parent.parent.parent. The query adds a new part of the where clause that refers to each node in the path.
E: shows a little logical structure that takes our PathProxy clause and adds an .entityType and .entityId to two different Strings. Next, we add those together and add them to the query. If we're not at the end of the path yet, we append an "and" (see F). Also, because we have a ManagedObject to deal with, we can easily get the objects' ids (see inline comments).
Stepping back, I want to point out that we're using the Class names for the objects as their type. This has a lot of synergy with JPA, which also uses the Class to identify objects. We could have added a getType() method to ManagedObject and used that. I've found, though, that using the classname is a good solution.
G: shows an else that handles a null in the path array. This should occur only at the end of the array. At the end of the query, it will add a "... and pp.parent.parent.parent is null". As the inline comments note, if you are dealing with composite keys, Hibernate doesn't output the correct SQL, and you will get an error. You can usually get away with leaving this off, so long as your root-level objects are unique for the paths in your system.
Now we're at the point where we can execute the query and see whether we have a PathProxy or not. If not, we'll either create one or return null, based on the createIfNotFound flag.

使用道具 举报

回复
论坛徽章:
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
20#
 楼主| 发表于 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.

使用道具 举报

回复

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

本版积分规则 发表回复

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