0

TL;DR:您如何使用 Spring JDBC 以最佳方式填充复杂的域模型?

我以前只使用 JPA 从数据库中检索内容,但我们的数据库管理员抱怨框架向数据库发送了多少查询以及它们的效率如何,因此在我们的新项目中,我们决定尝试使用 Spring JDBC。我开始使用每个实体一个查询的方法来实现对我们相当复杂的域模型的检索,但是将结果放在模型中它们所属的位置的逻辑变得很难很快地遵循。

例如:项目可以有许多影响它们的动作,而一个动作可以影响许多项目。当我获取一个项目时,我想查看它的操作,并且我还想查看它们受影响的项目,不包括我首先获取的项目。所以这个数据:

Item: | id |  name |  Action: | id |  actNo  | itemId |
      |  1 | 'One' |          |  1 | '001-1' |      1 |
      |  2 | 'Two' |          |  1 | '001-1' |      2 |
                              |  2 | '002-2' |      2 |

获取“二”时会产生这个结果:

Item {id: 2, name: 'Two',
  actionList: {
    Action {id: 1, actNo: '001-1',
      itemList: {
        Item {id: 1, name: 'One'}
      }
    },
    Action {id: 2, actNo: '002-2'}
  }
}

这是我到目前为止的代码:

@Transactional
public List<Item> getItems(List<Integer> idList) {
    initializeTempTable(idList);
    return runQueries();
}

private void initializeTempTable(List<Integer> idList) {
    String createSql = "create temporary table if not exists temp_table (id int) on commit delete rows";
    jdbcTemplate.update(createSql, (SqlParameterSource) null);

    String insertSql = "insert into temp_table (id) values (:value)";
    List<MapSqlParameterSource> parameters = new ArrayList<MapSqlParameterSource>(idList.size());
    for(Integer id : idList) {
        parameters.add(new MapSqlParameterSource("value", id));
    }
    jdbcTemplate.batchUpdate(insertSql, parameters.toArray(new SqlParameterSource[parameters.size()]));
}

private List<Item> runQueries() {
    List<Item> itemList = getItems();
    addActions(itemList);
    // Add the rest...
    return itemList;
}

private List<Item> getItems() {
    String sql = "select i.* from item i join temp_table t on i.id = t.id";
    return jdbcTemplate.query(sql, (SqlParameterSource) null, new RowMapper<Item>() {
        public Item mapRow(ResultSet rs, int rowNum) throws SQLException {
            Item item = new Item();
            item.setId(rs.getInt("id"));
            item.setName(rs.getString("name"));
            return item;
        }
    });
}

private void addActions(List<Item> itemList) {
    String sql = "select a.* from action a " + 
            "join item i on a.itemId = i.id " +
            "join temp_table t on i.id = t.id;

    final Map<Integer, List<Item>> resultMap = new HashMap<Integer, List<Item>>();

    jdbcTemplate.query(sql, (SqlParameterMap) null, new RowCallbackHandler() {
        public void processRow(ResultSet rs) throws SQLException {
            Action action = new Action();
            action.setId(rs.getInt("id"));
            action.setActNo(rs.getString("actNo"));

            int itemId = rs.getInt("itemId");
            if(resultMap.containsKey(itemId)) {
                List<Action> actionList = resultMap.get(itemId);
                actionList.add(action);
            } else {
                List<Action> actionList = new ArrayList<Action>(Arrays.asList(action));
                resultMap.put(itemId, actionList);
            }
        }
    });

    for(Item item : itemList) {
        List<Action> actionList = resultMap.get(item.getId());
        item.setActionList(actionList);
    }

    addItemsToActions(resultMap);
}

private void addItemsToActions(final Map<Integer, List<Action>> resultMap) {
    String sql = "select i2.*, a2.id as actionId, i.id as orgId from item i2 " +
            "join action a2 on i2.id = a2.itemId " +
            "join action a on a2.id = a.id " +
            "join item i on a.itemId = i.id " +
            "join temp_table t on i.id = t.id " +
            "where i2.id != i.id";

    jdbcTemplate,query(sql, (SqlParameterSource) null, new RowCallbackHandler() {
        public void processRow(ResultSet rs) throws SQLException {
            Item item = new Item();
            item.setId(rs.getInt("id"));
            item.setName(rs.getString("name"));

            int orgItemId = rs.getInt("orgId");
            if(resultMap.containsKey(orgItemId)) {
                List<Action> actionList = resultMap.get(orgItemId);
                int actionId = rs.getInt("actionId");
                for(Action action : actionList) {
                    if(action.getId() == actionId) {
                        if(action.getItemList() == null) {
                            action.setItemList(new ArrayList<Item>());
                        }
                        action.getItemList().add(item);
                        break;
                    }
                }
            }
        }
    });
}

如您所见,对于这样一个简单的关系,我得到了一些不明显的 sql 和许多难以理解的映射代码。我能看到如何解决这个问题的唯一方法就是完全按照 JPA 框架所做的那样:深度优先遍历模型并运行大量小查询来填充每个实例。这将使数据库管理员再次不高兴。

有没有更好的办法?

4

1 回答 1

0

不,没有更好的方法,如果你想要这样的查询 ORM 绝对不是要走的路(尽管一些 ORM 粉丝会告诉你)你最好将结果集作为动态平面结构返回,比如映射,并忘记尝试将其映射到域对象以及随之而来的所有父子噩梦。

上次我检查过 Spring 时有一个 queryForMap,尽管我不确定它是否返回一个类型化的 Map。

于 2015-10-23T14:54:31.980 回答