我正在研究Spring Core认证,并且对Spring如何处理JDBC查询有一些疑问:
因此,我知道可以根据希望获取的数据类型以各种方式从数据库表中获取数据:
1)查询简单类型(作为int,long或String):我使用 jdbcTemplate 类的 queryForObject()方法,类似这样:
String sql = "SELECT count(*) FROM T_REWARD";
int rowsNumber = jdbcTemplate.queryForObject(sql, Integer.class);
因此,要获得一个简单的对象作为int值,我使用 queryForObject()方法,将 sql语句传递给它,并希望从该方法的输出中接收该对象的类型。
好的,这很简单,我认为可以。
2)查询整个表中的整个行,并将其放入 Map 对象:因此,如果我不需要单个值(可以位于表的特定行的单个列中,或者像上一个示例一样),则可以通过以下方式使用 queryForMap(..)和 queryForList()方法:
2.1) queryForMap():如果我希望将单行放入一个单个Map对象中,则将其使用,其中每个列值都映射到我的Map中,例如
String sql = "select * from T_REWARD where CONFIRMATION_NUMBER = ?";
Map<String, Object> values = jdbcTemplate.queryForMap(sql,confirmation.getConfirmationNumber());
2.2) queryForList():如果希望将更多行作为查询的输出,则可以使用它。因此,我将获得Map对象的列表,其中每个Map对象都代表查询输出的特定行。像这样的东西:
String sql = “select * from PERSON”;
return jdbcTemplate.queryForList(sql);
我认为这也很清楚。
然后,我可以使用JdbcTemplate将 ResultSet 映射到域对象中,这对我来说还不是很清楚。
阅读该文档可得知JdbcTemplate使用回调方法支持此功能。这个回调方法到底是什么意思?
我知道Spring为提供了 RowMapper接口(interface),用于将ResultSet的单行映射到对象:
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum)
throws SQLException;
}
并且我有以下使用此方法的示例组合,该示例使用新的RestaurandRowMapper对象作为 queryForObject()方法的返回对象:
public Restaurant findByMerchantNumber(String merchantNumber) {
String sql = "select MERCHANT_NUMBER, NAME, BENEFIT_PERCENTAGE, BENEFIT_AVAILABILITY_POLICY from T_RESTAURANT where MERCHANT_NUMBER = ?";
return jdbcTemplate.queryForObject(sql, new RestaurantRowMapper(), merchantNumber);
这个内部类:
class RestaurantRowMapper implements RowMapper<Restaurant> {
public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
return mapRestaurant(rs);
}
}
使用此私有(private)方法创建映射的对象:
private Restaurant mapRestaurant(ResultSet rs) throws SQLException {
// get the row column data
String name = rs.getString("NAME");
String number = rs.getString("MERCHANT_NUMBER");
Percentage benefitPercentage = Percentage.valueOf(rs.getString("BENEFIT_PERCENTAGE"));
// map to the object
Restaurant restaurant = new Restaurant(number, name);
restaurant.setBenefitPercentage(benefitPercentage);
restaurant.setBenefitAvailabilityPolicy(mapBenefitAvailabilityPolicy(rs));
return restaurant;
}
所以我很难理解所有这些东西是如何工作的。
我的主要疑问是以下几点:我知道我使用 queryForObject()方法传递给它,作为输入参数,它是我期望作为输出的对象的类型(例如Interger或Long)。
如果我希望获得一个代表表的整个行的域对象(例如,映射到 Restaurand对象的餐厅表的行),则在考虑使用该对象(作为Restaurant对象)但在前面的示例中,我使用**行映射器对象而不是域对象:
return jdbcTemplate.queryForObject(sql, new RestaurantRowMapper(), merchantNumber);
此内部类仅包含 mapRow()方法,该方法返回预期的域对象
class RestaurantRowMapper implements RowMapper<Restaurant> {
public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
return mapRestaurant(rs);
}
}
因此,我认为Spring会自动调用 mapRow()方法,该方法返回 Restaurand域对象,该对象将自动替换为queryForObject()方法或类似的方法。但是我不确定它是否可以正常工作。
我想念什么?您能解释一下后台到底发生了什么吗?
特纳克斯
最佳答案
queryForObject
方法如下所示:
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));
return DataAccessUtils.requiredSingleResult(results);
}
queryForObject
方法在内部调用query
对象上的JdbcTemplate
方法。 query
方法定义为:public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException;
如您所见,
ResultSetExtractor<T>
传递给query
-method,Spring方便地将RowMapper<T>
转换为new RowMapperResultSetExtractor<T>(rowMapper, 1)
类型的对象。 RowMapperResultSetExtractor
是持有魔术 key 的对象。调用对象时,它将按照以下代码片段对所有行进行迭代:public List<T> extractData(ResultSet rs) throws SQLException {
List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
int rowNum = 0;
while (rs.next()) {
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
}
因此,您的原始
RowMapper
是为每一行调用的回调。此外,正如您在此处看到的那样,将为所有匹配的结果调用RowMapper
,并将您创建的Restaurant
-object添加到结果列表中。但是,由于仅查询一个对象,因此以下语句最终用于返回单个Restaurant
对象。 return DataAccessUtils.requiredSingleResult(results);
因此,总而言之:
JdbcTempalte
实现了Strategy Pattern(类似于Template method pattern)。通过提供一个Strategy接口(interface)(RowMapper
),您可以让JdbcTemplate
为您完成繁重的工作(处理异常,连接等)。 RowMapper
将每个匹配项映射为POJO(Restaurant
),所有匹配项都收集到List
中。然后,方法queryForObject
从该List
中获取第一行,并将其返回给调用方。返回值基于RowMapper
的通用类型,在您的情况下为Restaurant
。