Spring RowMapper 接口究竟是如何工作的



我正在学习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 接口,用于将 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(( 方法传递给它,作为输入参数,我希望作为输出的对象类型(例如 Interger 或 Long(。

如果我希望获得表示表的整行的域对象(例如映射到 Restaurand 对象的 Restaurant 的一行(,我认为我应该使用此对象(作为 Restaurant 对象(,但在前面的示例中,我使用 **row mapper 对象而不是域对象

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 域对象,或类似的东西。但我不太确定它是否完全有效。

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

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是持有魔法钥匙的对象。调用对象时,它会按照以下代码片段迭代所有行:

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实现了策略模式(类似于模板方法模式(。通过提供策略界面(RowMapper(,您可以JdbcTemplate为您完成繁重的工作(处理异常、连接等(。RowMapper将每个命中映射为POJO(Restaurant(,并将所有命中收集到List。然后,该方法queryForObject从该List中获取第一行,并将其返回给调用方。返回值基于RowMapper的泛型类型,在您的情况下为 Restaurant

使用 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;
}

最新更新