前言
虽然JdbcTemplate基本上不会在项目中用到,但是通过阅读和学习它的源码,可以提升我们面对接口编程的能力。JdbcTemplate提供多种query和update方法用来方便地查询和更新数据。
使用示例:
java
@Component
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public UserDao() {}
public String getUserName() {
User user = jdbcTemplate.queryForObject("select id,username,password from xxl_job_user where id = '1'", (rs, rowNum) -> {
User temp = new User();
temp.setId(rs.getInt("id"));
temp.setName(rs.getString("username"));
temp.setPassword(rs.getString("password"));
return temp;
});
return user.getName();
}
}
上述例子中,我们只要写好sql,再创建一个RowMapper接口的实现类对象,就成功从数据库中查到用户信息并封装成User对象。
查询流程
初始化StatementCallback
- 先将用户自定义的行映射器 (RowMapper)封装成结果集提取器 (ResultSetExtrator)
java
@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
}
- 再将sql字符串 和结果集提取器 封装成查询sql语句回调(QueryStatementCallback)。
java
@Override
@Nullable
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
/**
* Callback to execute the query.
*/
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
获取Statement
- 先获取一个数据源(DataSource对象)。
- 再通过数据源获取一个数据库连接(Connection对象)。
- 再通过数据库连接创建一个sql语句(Statement对象)
java
//-------------------------------------------------------------------------
// Methods dealing with static SQL (java.sql.Statement)
//-------------------------------------------------------------------------
@Override
@Nullable
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
执行StatementCallback的回调方法
- 执行sql语句得到**结果集**(ResultSet) 2. 将结果集作为参数,执行**结果集提取器**的extractData(),extractData()内部执行**行映射器**的mapRow(),将一行数据映射成对象。
总结
查询流程:
简单说:
- 先拿到Statement对象,Statement对象执行对应的sql得到结果集。
- 结果集提取器借助行映射器对结果集进行映射,得到对应的对象。
扩展
RowMapper、ResultSetExtrator、StatementCallback为什么要定义成泛型?
通过泛型将数据库返回的数据类型参数化,从而可以针对不同类型sql操作返回不同类型的对象。比如select语句,查询的表不一样,返回对象的类型也不一样,查询数据的数量不一样,返回的可以是单个对象,也可以是对象列表。又比如update语句,可以返回变更成功的数据个数,也可以返回是否变更成功,将返回数据的具体类型交给开发人员自行实现。