1、StatementHandler
StatementHandler接口定义包含创建Statement, 绑定参数,批量执行,更新,查询,获取参数处理器
类结构图为
PreparedStatementHandler用于处理带有占位符的语句
SimpleStatementHandler处理不带占位符的
CallableStatementHandler处理存储过程调用语句
1.1、RoutingStatementHandler
起代理作用,根据不同的语句类型创建不同BaseStatementHandler子类。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
1.2、BaseStatementHandler
是其它子类的基类,会创建ParameterHandler,ResultSetHandler。BaseStatementHandler依赖两个组件ParameterHandler和ResultSetHandler。
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
实现StatementHandlere接口中的prepare,首先调用instantiateStatement抽象方法初始化java.sql.Statement对象,配置超时时间和fetchSize。
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
1.3、SimpleStatementHandler
继承BaseStatementHandler抽象类,sql语句中没有占位符,相应的parameterize方法为空实现。
instantiateStatement直接通过JDBC Connection创建Statemtn对象。
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.createStatement();
} else {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
查询query 通过创建的Statement对象来完成数据库查询操作,结果通过ResultSetHandler将结果集映射成结果对象。
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
queryCursor,batch方法与query方法类似。也是直接调用Statement对象的对应方法。
update方法执行insert、update或delete等sql语句,并且会根据配置的KeyGenerator获取数据库的主键。
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
1.4、PreparedStatementHandler
依赖java.sql.PreparedStatement对象来完成数据库的相关操作。
parameterize方法会调用ParameterHandler.setParameters完成sql语句的参数绑定。
instantiateStatement方法会直接调用JDBC Connection的prepareStatement方法创建PreparedStatement对象。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
1.5、CallableStatementHandler
依赖于java.sql.CallableStatement调用指定的存储过程,其parameterize方法会调用ParameterHandler.setParameter方法完成sql语句的参数绑定,并指定输出参数的索引位置和JDBC类型。
public void parameterize(Statement statement) throws SQLException {
registerOutputParameters((CallableStatement) statement);
parameterHandler.setParameters((CallableStatement) statement);
}
private void registerOutputParameters(CallableStatement cs) throws SQLException {
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0, n = parameterMappings.size(); i < n; i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
if (null == parameterMapping.getJdbcType()) {
throw new ExecutorException("The JDBC Type must be specified for output parameter. Parameter: " + parameterMapping.getProperty());
} else {
if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
} else {
if (parameterMapping.getJdbcTypeName() == null) {
cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
} else {
cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());
}
}
}
}
}
}
instantiateStatement直接调用JDBC Connection的prepareCall创建CallableStatement对象。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareCall(sql);
} else {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
其它方法与以上有一些差异,就是会调用ResultSetHandler的handleOutputParameters处理输出参数
public int update(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
int rows = cs.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
resultSetHandler.handleOutputParameters(cs);
return rows;
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
Cursor<E> resultList = resultSetHandler.handleCursorResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
2、ParameterHandler
主要包含绑定参数,及获取参数对象。
DefaultParameterHandler是唯一实现。
绑定参数时,遍历boundSql中的参数映射,忽略输出类型参数。提取值基于以下规则
- 属性名是否在boundSql中的附加参数中,如果有,则直接从map中取值。
- 参数对象为空时,值设置为null
- TypeHandlerRegistry中是否参参数对象所必类型的处理器,有则直接取参数对象(说明是基础类型)
- 基于MetaObject获取属性值。
使用TypeHandler来设置PreparementStatement对应的值。
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}