Java 用SpringJDBC填充完整的域模型

Java 用SpringJDBC填充完整的域模型,java,jdbc,spring-jdbc,Java,Jdbc,Spring Jdbc,TL;DR:如何使用SpringJDBC以最佳方式填充复杂的域模型 我以前只使用JPA从数据库中检索内容,但我们的db管理员抱怨框架向数据库发送了多少查询,而且效率很低,因此在我们的新项目中,我们决定试用SpringJDBC。我开始使用每个实体一个查询的方法来实现对我们相当复杂的领域模型的检索,但是将结果放在模型中所属位置的逻辑很难很快遵循 例如:项可以有多个操作影响它们,一个操作可以影响多个项。当我获取一个项目时,我希望看到它的操作,我还希望看到它们受影响的项目,不包括我首先获取的项目。因此,

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

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

例如:项可以有多个操作影响它们,一个操作可以影响多个项。当我获取一个项目时,我希望看到它的操作,我还希望看到它们受影响的项目,不包括我首先获取的项目。因此,这些数据:

Item: | id |  name |  Action: | id |  actNo  | itemId |
      |  1 | 'One' |          |  1 | '001-1' |      1 |
      |  2 | 'Two' |          |  1 | '001-1' |      2 |
                              |  2 | '002-2' |      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;
                    }
                }
            }
        }
    });
}
@Transactional
公共列表getItems(列表idList){
可初始化(idList);
返回runquerys();
}
private void initializeTempTable(列表idList){
String createSql=“如果提交删除行上不存在临时表(id int),则创建临时表”;
update(createSql,(SqlParameterSource)null);
String insertSql=“插入临时表(id)值(:值)”;
列表参数=新的ArrayList(idList.size());
for(整数id:idList){
添加(新的MapSqlParameterSource(“value”,id));
}
batchUpdate(insertSql,parameters.toArray(新的SqlParameterSource[parameters.size()]);
}
私有列表runquerys(){
List itemList=getItems();
添加操作(项目列表);
//加上剩下的。。。
返回项目列表;
}
私有列表getItems(){
String sql=“从i.id=t.id上的项目i连接临时表t中选择i.*”;
返回jdbcTemplate.query(sql,(SqlParameterSource)null,新的行映射器(){
公共项mapRow(ResultSet rs,int rowNum)引发SQLException{
项目=新项目();
项目.setId(rs.getInt(“id”);
item.setName(rs.getString(“名称”));
退货项目;
}
});
}
私有void addActions(列表项列表){
String sql=“从操作a中选择一个*”+
“在a.itemId=i.id上加入项目i”+
“i.id=t.id上的连接临时表t;
最终映射结果映射=新HashMap();
查询(sql,(SqlParameterMap)null,新的RowCallbackHandler(){
public void processRow(结果集rs)引发SQLException{
动作动作=新动作();
action.setId(rs.getInt(“id”);
action.setActNo(rs.getString(“actNo”);
int itemId=rs.getInt(“itemId”);
if(结果映射containsKey(itemId)){
List actionList=resultMap.get(itemId);
操作列表。添加(操作);
}否则{
List actionList=newarraylist(Arrays.asList(action));
结果映射put(itemId,actionList);
}
}
});
用于(项目:项目列表){
List actionList=resultMap.get(item.getId());
item.setActionList(操作列表);
}
添加操作(结果映射);
}
私有void addItemsToActions(最终映射结果映射){
String sql=“选择i2.*,a2.id作为actionId,i.id作为项目i2中的orgId”+
“在i2.id=a2.itemId上加入操作a2”+
“在a2.id=a.id上加入操作a”+
“在a.itemId=i.id上加入项目i”+
“i.id=t.id上的联接临时表t”+
“其中i2.id!=i.id”;
jdbcTemplate,查询(sql,(SqlParameterSource)null,新的RowCallbackHandler(){
public void processRow(结果集rs)引发SQLException{
项目=新项目();
项目.setId(rs.getInt(“id”);
item.setName(rs.getString(“名称”));
int orgItemId=rs.getInt(“orgId”);
if(结果映射containsKey(orgItemId)){
List actionList=resultMap.get(orgItemId);
int actionId=rs.getInt(“actionId”);
for(操作:操作列表){
if(action.getId()==actionId){
if(action.getItemList()==null){
action.setItemList(新的ArrayList());
}
action.getItemList().add(项);
打破
}
}
}
}
});
}
正如你所看到的,对于这样一个简单的关系,我得到了一些不明显的sql和许多难以理解的映射代码。我能看到如何解决这一问题的唯一方法是完全按照JPA框架所做的:首先遍历模型深度,然后运行许多小查询来填充每个实例。这将使db管理员不高兴又来了


有更好的方法吗?

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


上次我检查Spring时,它有一个queryForMap,虽然我不确定它是否返回类型化映射。

让一个Form框架来为您完成。管理所有这些代码将是一件难事。似乎您得到了许多复杂的查询,因为实体之间的关联不是惰性的,您现在尝试以某种方式重新实现这些非惰性查询这将更加低效,更加复杂,更容易出错。我会回到JPA,在默认情况下让一切都变得懒惰,并在需要的地方进行优化。如果你了解JPA在幕后的工作方式,很容易使其快速运行。如果你不懂,你会有愤怒的DBA。
@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;
                    }
                }
            }
        }
    });
}