Java Spring RowMapper 接口究竟是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27591847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How exactly work the Spring RowMapper interface?
提问by
I am studying for the Spring Core certification and I have some doubts about how Spring handle the JDBC queries:
我正在学习 Spring Core 认证,我对 Spring 如何处理 JDBC 查询有一些疑问:
So I know that I can obtain data from my DB tables in various ways depending on the type of data that I expect to obtain:
所以我知道我可以通过各种方式从我的数据库表中获取数据,具体取决于我期望获取的数据类型:
1) Query for simple type(as an int, a long or a String): I use the queryForObject()method of the jdbcTemplateclass, something like it:
1) 查询简单类型(作为 int、long 或 String):我使用jdbcTemplate类的queryForObject()方法,类似于:
String sql = "SELECT count(*) FROM T_REWARD";
int rowsNumber = jdbcTemplate.queryForObject(sql, Integer.class);
So to obtain a simple object as an int value I use the queryForObject()method passing to it the sql stattmentand the object type of I expect to receive in output from the method.
因此,为了获得一个简单的对象作为 int 值,我使用queryForObject()方法将sql stattment和我希望从该方法的输出中接收到的对象类型传递给它。
Ok, this is pretty simple and I think that it is ok.
好的,这很简单,我认为没问题。
2) Query for an entire table rowputted into a Mapobject: so if I need no a single value (that could be into a single column of a specific row of a table or something like the preovious example) I can use the queryForMap(..)and the queryForList()methods, in these way:
2)查询放入Map对象的整个表行:因此,如果我不需要单个值(可以是表的特定行的单个列或类似于前面的示例),我可以使用queryForMap( ..)和queryForList()方法,以这些方式:
2.1) queryForMap(): I use it if I expect a single rowputted into a single Map objectwhere each columns value is mapped into a of my Map, something like:
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(): I use it if I expect more rows as output of my query. So I will obtain a list of Map objectswhere each Map object represent a specific row of the query output. Something like it:
2.2) queryForList():如果我希望有更多行作为查询的输出,我会使用它。所以我将获得一个Map 对象列表,其中每个 Map 对象代表查询输出的特定行。类似的东西:
String sql = “select * from PERSON”;
return jdbcTemplate.queryForList(sql);
I think that also this is pretty clear.
我认为这也很清楚。
Then I can map a ResultSetinto a domain objectusing JdbcTemplate and this is not so clear for me.
然后我可以使用 JdbcTemplate将ResultSet映射到域对象中,这对我来说不是很清楚。
Reading the documentation is told that JdbcTemplate supports this using a callback approach. What exactly meand this callback approach?
阅读文档被告知 JdbcTemplate 使用回调方法支持这一点。这种回调方法究竟是什么意思?
I know that Spring provides a RowMapper interfacefor mapping a single row of a ResultSet to an object:
我知道 Spring 提供了一个RowMapper 接口,用于将 ResultSet 的单行映射到一个对象:
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum)
throws SQLException;
}
and I have the following example compoese by this method that use a new RestaurandRowMapper object as returned object of a queryForObject()method:
并且我通过此方法编写了以下示例,该方法使用新的 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);
and this inner class:
和这个内部类:
class RestaurantRowMapper implements RowMapper<Restaurant> {
public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
return mapRestaurant(rs);
}
}
that use this private method to create the mapping:
使用这个私有方法来创建映射:
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;
}
So I have some difficulties to understand how exactly work all this stuff.
所以我很难理解这些东西究竟是如何工作的。
My main doubt is the following one: I know that using the queryForObject()method I pass to it, as input parameter, the type of the object that I expect as output (for example an Interger or a Long).
我的主要疑问是以下一个:我知道使用queryForObject()方法作为输入参数传递给它,我希望作为输出的对象的类型(例如 Interger 或 Long)。
If I expect to obtain a domain object that represent an entire row of a table (for example a row of the Restaurant tablemapped into a Restaurand object) I'm thinking that I should use this object (as the Restaurant object) but in the previous example I use the **row mapper objectinstead the domain object:
如果我希望获得一个代表整行表的域对象(例如,映射到Restaurand 对象的Restaurant 表的一行),我想我应该使用这个对象(作为 Restaurant 对象),但在前面的示例我使用 **row 映射器对象而不是域对象:
return jdbcTemplate.queryForObject(sql, new RestaurantRowMapper(), merchantNumber);
This inner class contain only the mapRow()method that return the expected domain object
这个内部类只包含返回预期域对象的mapRow()方法
class RestaurantRowMapper implements RowMapper<Restaurant> {
public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
return mapRestaurant(rs);
}
}
So I think that Spring automatically call the mapRow()method that return the Restaurand domain objectthat is automatically replaced into the queryForObject() method, or something like this. But I am not so sure about it exactly work.
所以我认为 Spring 会自动调用mapRow()方法,该方法返回自动替换为 queryForObject() 方法的Restaurand 域对象,或者类似的东西。但我不太确定它是否完全有效。
What am I missing? Can you explain me what exactly happen in the backstage?
我错过了什么?你能解释一下后台到底发生了什么吗?
Tnx
Tnx
采纳答案by wassgren
The queryForObject
method looks like this:
该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);
}
The queryForObject
-method internally invokes the method query
on the JdbcTemplate
object. The query
-method is defined as:
所述queryForObject
-method内部调用该方法query
的上JdbcTemplate
对象。所述query
-method定义为:
public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException;
As you can see, a ResultSetExtractor<T>
is passed to the query
-method and Spring conveniently converts your RowMapper<T>
to an object of type new RowMapperResultSetExtractor<T>(rowMapper, 1)
. The RowMapperResultSetExtractor
is the object that holds the key to the magic. When the object is invoked it iterates all rows as per this snippet:
如您所见, aResultSetExtractor<T>
被传递给query
-method 并且 Spring 方便地将您的转换RowMapper<T>
为 type 的对象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;
}
So, your original RowMapper
is the callback that is called for each row. Furthermore, as you can see here your RowMapper
is invoked for all matching results and the Restaurant
-object that you created is added to the result list. However, since you query for only oneobject the following statement is finally used to return your single Restaurant
object.
因此,您的原始内容RowMapper
是为每一行调用的回调。此外,正如您在此处看到的,您RowMapper
会为所有匹配的结果调用您,并将Restaurant
您创建的-object 添加到结果列表中。但是,由于您只查询一个对象,因此以下语句最终用于返回您的单个Restaurant
对象。
return DataAccessUtils.requiredSingleResult(results);
So, to sum up: JdbcTempalte
implementes the Strategy Pattern(which is similar to the Template method pattern). And by providing a Strategy interface(the RowMapper
) you can let JdbcTemplate
do the heavy lifting for you (handling exceptions, connections etc). The RowMapper
maps each hit as a POJO (the Restaurant
) and all hits are collected to a List
. The method queryForObject
then takes the first row from that List
and returns it to the caller. The return value is based on the generic type of the RowMapper
which in your case is Restaurant
.
所以,总结一下:JdbcTempalte
实现了策略模式(类似于模板方法模式)。通过提供Strategy 接口(the RowMapper
),您可以JdbcTemplate
为您完成繁重的工作(处理异常、连接等)。在RowMapper
每个命中映射为一个POJO(中Restaurant
)和所有命中收集到List
。queryForObject
然后该方法从中获取第一行List
并将其返回给调用者。返回值基于RowMapper
在您的情况下是的泛型类型Restaurant
。
回答by rajkumar chilukuri
Use jdbcTemplate.queryForObject it will resolve your problem like this
使用 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;
}