TL; DR:如何使用Spring JDBC以最佳方式填充复杂域模型?使用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框架所做的工作:首先遍历模型深度,然后运行大量小查询,以便在每个实例出现时填充它们。这将使数据库管理员再次不高兴。
有没有更好的方法?
让一个ORM框架为你做。管理所有的代码会很丑陋。 – 2013-04-10 16:27:04
看起来你有很多复杂的查询,因为实体之间的关联不是懒惰的,你现在试图用一种效率更低,更复杂,更容易出错的方式重新实现这些非惰性查询。我会回到JPA,默认情况下默认为一切,并在需要时进行优化。如果您了解JPA如何在幕后进行操作,则很容易使其变得更快。如果你不理解它,你会有愤怒的DBA。 – 2013-04-10 17:35:40