JDBCTemplate是使用模板设计模式的典范。

通过使用接口做一定的抽象,避免了jdbc的样板代码。

关于jdbc的样板代码这里就不做介绍了,大家应该都知道,这里直接看JDBCTemplate。

String sql = "select * from user";
jdbcTemplate.query("", new Object[]{},new RowMapper<Object>() {
@Override
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
return null;
}
});

这里举一个query的例子,一般我们都是像上面这种方式来使用的,下面看下query的实现:

public <T> List<T> query(String sql, @Nullable Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
return (List)result(this.query((String)sql, (Object[])args, (ResultSetExtractor)(new RowMapperResultSetExtractor(rowMapper))));
}

这一步,将我们的rowMapper封装在一个Extractor里:

public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {
private final RowMapper<T> rowMapper;
private final int rowsExpected;

public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
this(rowMapper, 0);
}

public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
Assert.notNull(rowMapper, "RowMapper is required");
this.rowMapper = rowMapper;
this.rowsExpected = rowsExpected;
}

public List<T> extractData(ResultSet rs) throws SQLException {
List<T> results = this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList();
int var3 = 0;

while(rs.next()) {
results.add(this.rowMapper.mapRow(rs, var3++));
}

return results;
}
}

这里有一个extractData方法,调用了传入的rowMapper来生成一个list。应该会在query里面被调用。

再往下看query方法:

@Nullable
public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
return this.query(sql, this.newArgPreparedStatementSetter(args), rse);
}

这里又将我们传入的sql参数封装在一个Setter里:

protected PreparedStatementSetter newArgPreparedStatementSetter(@Nullable Object[] args) {
return new ArgumentPreparedStatementSetter(args);
}

public class ArgumentPreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {
@Nullable
private final Object[] args;

public ArgumentPreparedStatementSetter(@Nullable Object[] args) {
this.args = args;
}

public void setValues(PreparedStatement ps) throws SQLException {
if (this.args != null) {
for(int i = 0; i < this.args.length; ++i) {
Object arg = this.args[i];
this.doSetValue(ps, i + 1, arg);
}
}

}

protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
if (argValue instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue)argValue;
StatementCreatorUtils.setParameterValue(ps, parameterPosition, paramValue, paramValue.getValue());
} else {
StatementCreatorUtils.setParameterValue(ps, parameterPosition, -2147483648, argValue);
}

}

public void cleanupParameters() {
StatementCreatorUtils.cleanupParameters(this.args);
}
}

其内部有一个setValues方法,入参是我们传入的sql语句,然后该方法内部会将参数写入sql语句中。

再往下看query:

@Nullable
public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
return this.query((PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql)), (PreparedStatementSetter)pss, (ResultSetExtractor)rse);
}

这一步将传入的string的sql语句封装在一个creator里:

private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
private final String sql;

public SimplePreparedStatementCreator(String sql) {
Assert.notNull(sql, "SQL must not be null");
this.sql = sql;
}

public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(this.sql);
}

public String getSql() {
return this.sql;
}
}

内部的createPrepareStatement方法就生成了原生的jdbc里的statment语句。

再往下:

@Nullable
public <T> T query(PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(rse, "ResultSetExtractor must not be null");
this.logger.debug("Executing prepared SQL query");
return this.execute(psc, new PreparedStatementCallback<T>() {
@Nullable
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;

Object var3;
try {
if (pss != null) {
pss.setValues(ps);
}

rs = ps.executeQuery();
var3 = rse.extractData(rs);
} finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer)pss).cleanupParameters();
}

}

return var3;
}
});
}

这里使用了一个PreparedStatementCallback封装了之前的sql的creator,setter以及rowmapper,将他们组合在一起调用execute方法。该方法是一个偏内部的方法,虽然暴露给了外部,但是不应该直接调用,除非特殊需求。因为需要知道太多template内部实现了。

@Nullable
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException {
Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (this.logger.isDebugEnabled()) {
String sql = getSql(psc);
this.logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
}

Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
PreparedStatement ps = null;

Object var13;
try {
ps = psc.createPreparedStatement(con);
this.applyStatementSettings(ps);
T result = action.doInPreparedStatement(ps);
this.handleWarnings((Statement)ps);
var13 = result;
} catch (SQLException var10) {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer)psc).cleanupParameters();
}

String sql = getSql(psc);
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, this.getDataSource());
con = null;
throw this.translateException("PreparedStatementCallback", sql, var10);
} finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer)psc).cleanupParameters();
}

JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, this.getDataSource());
}

return var13;
}

从这里大致可以看到template的模式:

将传入的sql生成一条jdbc的statment并且设置参数(如果需要的话);

执行;

将结果调用mapper转换。

 

除了queryForObject以外,其与方法都是返回一个list的。

 

execute方法封装了样板代码,这样只有这一个函数有样板代码,其他的函数都只负责自己的逻辑,包括生成sql,插入参数,结果转换等等。这些就是抽象部分,也是模板设计模式的体现。

 

query函数的api:

query:返回一个list,一般需要传入一个mapper。一般都用这个方法,可以定制结果转换。

queryForObject:返回一个object。两种用法,一种是传入一个class,通常用来返回一个标量值,比如count,sum这种,具体类型由传入的参数类型来指定,最终在底层实现上会由一个SingleMapper来转换结果,原理大概就是直接将返回的sql结果集转成传入的类型。

另一种也是传入一个mapper,可以直接返回一个POJO。

queryForList:其实是上面方法的list版本。