Spring RowMapper接口究竟如何工作?

时间:2014-12-21 17:22:25

标签: java spring spring-jdbc jdbctemplate

我正在学习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 stattment 和我期望收到的对象类型传递给它该方法的输出。

好的,这很简单,我认为没问题。

2)查询整个表格行放入地图对象:所以如果我不需要任何一个值(可以放入特定的一个列中)表格的一行或类似于前面的例子)我可以用以下方式使用 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;
}

我在这个方法中有以下示例compoese,它使用一个新的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()方法我将其作为输入参数传递给它作为输出的对象类型(例如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()方法,该方法返回自动替换为queryForObject()方法的 Restaurand域对象,或类似的东西。但我不确定它究竟是否奏效。

我错过了什么?你能解释一下后台到底发生了什么吗?

TNX

2 个答案:

答案 0 :(得分:7)

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对象上的方法JdbcTemplatequery - 方法定义为:

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是掌握魔法关键的对象。调用对象时,它会按照此代码段迭代所有行:

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 - 对象已添加到结果列表中。但是,由于您只查询一个对象,因此以下语句最终用于返回单个Restaurant对象。

        return DataAccessUtils.requiredSingleResult(results);

所以,总结一下:JdbcTempalte实现Strategy Pattern(类似于Template method pattern)。通过提供策略界面RowMapper),您可以让JdbcTemplate为您做繁重的工作(处理异常,连接等)。 RowMapper每个点击一次作为POJO(Restaurant),所有点击都会收集到List。然后,方法queryForObject从该List获取第一行并将其返回给调用者。返回值基于RowMapper的泛型类型,在您的情况下为Restaurant

答案 1 :(得分:0)

使用jdbcTemplate.queryForObject,它将像你一样解决你的问题

IN