Spring RowMapper接口究竟如何工作?

7 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);
Run Code Online (Sandbox Code Playgroud)

因此,为了获得一个简单的对象作为int值,我使用queryForObject()方法向它传递sql stattment和我期望在该方法的输出中接收的对象类型.

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

2)查询放入Map对象的整个表行:所以如果我不需要单个值(可能是某个表的特定行的单个列或类似于preovious示例的东西),我可以使用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());
Run Code Online (Sandbox Code Playgroud)

2.2)queryForList():如果我希望有更多行作为我的查询输出,我会使用它.因此,我将获得一个Map对象列表,其中每个Map对象代表查询输出的特定行.喜欢它的东西:

String sql = “select * from PERSON”;
return jdbcTemplate.queryForList(sql);
Run Code Online (Sandbox Code Playgroud)

我认为这也很清楚.

然后我可以使用JdbcTemplate 将ResultSet映射到域对象,这对我来说并不是那么清楚.

阅读文档告诉JdbcTemplate使用回调方法支持这一点.究竟是什么以及这种回调方式

我知道Spring提供了一个RowMapper接口,用于将ResultSet的单行映射到对象:

public interface RowMapper<T> {
    T mapRow(ResultSet rs, int rowNum)
    throws SQLException;
}
Run Code Online (Sandbox Code Playgroud)

我有以下示例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);
Run Code Online (Sandbox Code Playgroud)

而这个内部阶级:

class RestaurantRowMapper implements RowMapper<Restaurant> {
    public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
        return mapRestaurant(rs);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此私有方法创建映射:

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;
}
Run Code Online (Sandbox Code Playgroud)

所以我有一些困难要理解这些东西究竟是如何工作的.

我主要怀疑的是:我知道使用queryForObject()方法,我将其作为输入参数传递给它,我期望作为输出的对象的类型(例如,Interger或Long).

如果我希望获得一个表示表的整行的域对象(例如,映射到Restaurand对象Restaurant表的一行)我认为我应该使用这个对象(作为Restaurant对象)但是在前面的例子我使用**行映射器对象而不是域对象:

return jdbcTemplate.queryForObject(sql, new RestaurantRowMapper(), merchantNumber);
Run Code Online (Sandbox Code Playgroud)

此内部类仅包含返回预期域对象mapRow()方法

class RestaurantRowMapper implements RowMapper<Restaurant> {
    public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
        return mapRestaurant(rs);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我认为Spring会自动调用mapRow()方法,该方法返回自动替换为queryForObject()方法的Restaurand域对象,或类似的东西.但我不确定它究竟是否奏效.

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

TNX

was*_*ren 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);
}
Run Code Online (Sandbox Code Playgroud)

所述queryForObject-method内部调用该方法query的上JdbcTemplate对象.所述query-method定义为:

public <T> T query(
        PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
        throws DataAccessException;
Run Code Online (Sandbox Code Playgroud)

如您所见,a ResultSetExtractor<T>传递给query-method,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;
}
Run Code Online (Sandbox Code Playgroud)

所以,你的原始版本RowMapper是为每一行调用的回调.此外,正如您在此处所见,您RowMapper将调用所有匹配结果,并将Restaurant您创建的对象添加到结果列表中.但是,由于您只查询一个对象,因此最后使用以下语句返回单个Restaurant对象.

        return DataAccessUtils.requiredSingleResult(results);
Run Code Online (Sandbox Code Playgroud)

因此,总结一下:JdbcTempalte实现策略模式(类似于模板方法模式).通过提供一个策略界面(the RowMapper),您可以JdbcTemplate为您做繁重的工作(处理异常,连接等).在RowMapper每打一个POJO(地图Restaurant)和所有命中收集到List.queryForObject然后该方法从该行获取第一行List并将其返回给调用者.返回值是基于通用型的RowMapper而你的情况是Restaurant.