Spring RowMapper 接口究竟是如何工作的?
Posted
技术标签:
【中文标题】Spring RowMapper 接口究竟是如何工作的?【英文标题】:How exactly work the Spring RowMapper interface? 【发布时间】:2015-02-19 21:53:48 【问题描述】:我正在学习 Spring Core 认证,我对 Spring 如何处理 JDBC 查询有一些疑问:
所以我知道我可以根据我期望获得的数据类型以各种方式从我的数据库表中获取数据:
1) 查询简单类型(作为int、long或String):我使用jdbcTemplate的queryForObject()方法strong> 类,类似的东西:
String sql = "SELECT count(*) FROM T_REWARD";
int rowsNumber = jdbcTemplate.queryForObject(sql, Integer.class);
因此,为了获得一个简单的对象作为 int 值,我使用 queryForObject() 方法将 sql stattment 和我希望接收的对象类型传递给它方法的输出。
好的,这很简单,我认为没问题。
2) 查询放入 Map 对象的整个表行:所以如果我不需要单个值(可以放入特定的单个列中)表的行或类似前面的示例)我可以通过这些方式使用 queryForMap(..) 和 queryForList() 方法:
2.1) queryForMap():如果我希望将 单行 放入 单 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 接口,用于将 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 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() 方法作为输入参数传递给它,我期望作为输出的对象的类型(例如整数或长整数)。
如果我希望获得一个代表整行表的域对象(例如映射到 Restaurand 对象的 Restaurant 表 的一行),我想我应该使用这个对象(作为 Restaurant 对象),但在前面的示例中,我使用 **row mapper 对象 而不是 domain 对象:
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() 方法或类似的东西.但我不太确定它是否真的有效。
我错过了什么?你能解释一下后台到底发生了什么吗?
Tnx
【问题讨论】:
【参考方案1】: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
-方法在内部调用 JdbcTemplate
对象上的方法 query
。 query
-方法定义为:
public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException;
如您所见,ResultSetExtractor<T>
被传递给query
方法,Spring 方便地将您的RowMapper<T>
转换为new RowMapperResultSetExtractor<T>(rowMapper, 1)
类型的对象。 RowMapperResultSetExtractor
是持有魔法钥匙的对象。当对象被调用时,它会按照这个 sn-p 迭代所有行:
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)。通过提供策略接口(RowMapper
),您可以让JdbcTemplate
为您完成繁重的工作(处理异常、连接等)。 RowMapper
将每个命中映射为 POJO(Restaurant
),所有命中都收集到 List
。方法queryForObject
然后从List
中取出第一行并将其返回给调用者。返回值基于RowMapper
的泛型类型,在您的情况下为Restaurant
。
【讨论】:
【参考方案2】:使用 jdbcTemplate.queryForObject 它会像这样解决你的问题
public YourPojo getDatabaseDetails(int masterId)
sql = "Select * FROM <table_name> where ID=?";
YourPojo pojo = (YourPojo) jdbcTemplate.queryForObject(sql,
new Object[] masterId , new RowMapper<YourPojo>()
@Override
public <YourPojo> mapRow(ResultSet rs, int rowNum)
throws SQLException
YourPojo pojo2 = new YourPojo();
pojo2.setAccountId(rs.getString("AccountId"));
pojo2.setAccountName(rs.getString("AccountName"));
pojo2.setAccountCRN(rs.getString("AccountCRN"));
pojo2.setAccountStatus(rs.getString("AccountStatus"));
return pojo2;
);
return pojo;
【讨论】:
以上是关于Spring RowMapper 接口究竟是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章
Spring BeanPostProcessor 究竟是如何工作的?
Spring DI applicationContext.xml xsi:schemaLocation 究竟是如何使用的?
RESTful 应用程序的 @ResponseStatus Spring 注释究竟是如何工作的?