使用jdbc 访问数据库按以下流程进行:
1. 准备资源
2. 启动事务
3. 在事务中执行具体数据库访问操作
4. 提交或回滚事务
5. 关闭资源,处理异常。
按照传统方式,编写任何带事务的数据访问程序时,都要重复编写上面代码。除了3是和业务相关的,1,2,4,5 步骤都一样。
所以spring 将相同的数据访问流程固化到模版类,并将数据访问中固定和变化的部分分开,同时保证模版类是线程安全的,以便多个线程访问同一个模版实例出现异常。固定的部分已在模版类准备好,而变化的部分通过回调接口开放出来,用于定义具体数据访问和结果返回操作。
spring 为不同的持久化技术提供了不同的模版类
ORM持久化技术 | 模版类 |
---|---|
JDBC | org.springframework.jdbc.core.JdbcTemplate |
iBatis | org.springframework.orm.ibatis.SqlMapClientTemplate |
等等。
我们以JdbcTemplate为例
@Override
public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback());
}
这个方法是最常用的,参数为要执行的sql语句,内部调用的是
@Override
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
//当前线程持有的数据库连接,保证线程安全
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
//构造statement
stmt = conToUse.createStatement();
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
//调用回调函数执行sql
T result = action.doInStatement(stmtToUse);
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.
JdbcUtils.closeStatement(stmt);
stmt = null;
//释放数据库连接
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
execute方法用了模版设计模式,将获取数据库连接,创建statement,释放数据库连接封装起来,这些是固定部分;传入的sql语句通过回调传入方法中,这是变化部分。