mybatis源码解读(⼋):Statement语句执⾏详解
功能
mybatis执⾏语句的操作是由StatementHandler完成的,它会去获取连接并且根据你配置的⼀些参数来准备好连接数据库的statment语句,然后通过StatementHandler执⾏语句并且将结果根据你配置的ResultMap封装成为你想要的实体类。
StatementHandler完成的操作:
获取连接,根据配置准备数据库可执⾏的statement语句。
执⾏语句
通过ResultSetHandler根据resultMapping封装成为你想要的的实体对象
UML
StatementHandler的⼦类主要有三个,这三个是根据Configuration配置的StatementType策略去分别⽣成的SimpleStatementHandler StatementType.STATEMENT 普通的statment,有sql注⼊的风险
PreparedStatementHandler StatementType.PREPARED 预处理statment
CallableStatementHandler StatementType.CALLABLE 可使⽤执⾏器
实现StatementHandler的还有BaseStatementHandler,这是⼀个基础类,这个类中包含了⼀些公⽤的功能RoutingStatementHandler 只是⼀个路由,它内部有⼀个策略然后去执⾏不同的statment
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, Boun dSql 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());
}
}
代码解析
构建 StatementHandler 是在 wStatementHandler()这个⽅法中完成的,最终会⽣成⼀个RoutingStatementHandler 代理类,delegate 是具体的StatementHandler ,会根据MappedStatement配置的策略去⽣成,⽅法最后会将handler放到插件中去拦截
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBound s, ResultHandler resultHandler, BoundSql boundSql){
// ⽣成⼀个routing类
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, bound Sql);
statementHandler =(StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
prepareStatement()⽅法中,逻辑也很清晰,第⼀步是获取连接,第⼆步是⽣成statment语句,第三步是将⼊参放⼊到⽣成的statement 语句中。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, Timeout());
handler.parameterize(stmt);
return stmt;
}
getConnection()⽅法中,其实是调⽤了JdbcTransaction的getConnection()⽅法,⽽在这个⽅法中⼜调⽤了openConnection(),在openConnection()中会去调⽤java的API 原⽣datasource去获取连接,在连接成功后就是设置事务的隔离级别以及是否⾃动提交事务等属性。
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = Connection();
}
// JdbcTransaction
public Connection getConnection() throws SQLException {
if(connection == null){
openConnection();
}
return connection;
}
// JdbcTransaction
protected void openConnection() throws SQLException {
/
/ 原⽣api
connection = Connection();
// 隔离级别
if(level != null){
connection.Level());
}
// 事务是否⾃动提交
setDesiredAutoCommit(autoCommit);
}
handler.prepare()⽅法完成的功能就是初始化statment语句
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
/.../
// 初始化
statement = instantiateStatement(connection);
// 设置超时时间
setStatementTimeout(statement, transactionTimeout);
// 设置每次获取的⼤⼩
setFetchSize(statement);
return statement;
/.../
}
instantiateStatement()⽅法中,会根据你的配置去设置⼀些statment语句配置,⽐如如果你配置了key
Generate,他会去根据你配置的要⾃动⽣成的数据库列名来为你⾃动⽣成值并且设置主键值,然后就是如果你设置了ResultSetType类型,则会根据你的类型去⽣成statement,这些都是java原⽣操作。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = Sql();
// 是否配置了主键⽣成策略
KeyGenerator() instanceof Jdbc3KeyGenerator){
String[] keyColumnNames = KeyColumns();
if(keyColumnNames == null){
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
}else{
return connection.prepareStatement(sql, keyColumnNames);
}
// 是否配置了resultSetType
}else ResultSetType()== ResultSetType.DEFAULT){
return connection.prepareStatement(sql);
}else{
return connection.prepareStatement(sql, ResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
设置超时时间,这⾥的超时时间有个⽣效的优先级,⾸先是在事务管理器中配置的超时时间,然后是在mapper中配置的超时时间,最后才是configuration中配置的超时时间,可以从逻辑中看到,最终会有个 transactionTimeout < queryTimeout 这样的⽐较,这是为了保证事务中设置的超时时间优先。
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
Integer queryTimeout = null;
// mapper中设置的超时时间为第⼆优先级
Timeout()!= null){
queryTimeout = Timeout();
// 最后是configuration的默认超时时间
}else DefaultStatementTimeout()!= null){
queryTimeout = DefaultStatementTimeout();
}
if(queryTimeout != null){
stmt.setQueryTimeout(queryTimeout);
}
/
/ 第⼀优先级是事务中设置的超时时间
StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
}
public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout) throws SQLException {
if(transactionTimeout == null){
return;
}
if(queryTimeout == null || queryTimeout == 0 || transactionTimeout < queryTimeout){
statement.setQueryTimeout(transactionTimeout);
}
}
prepare()处理完之后,回到上个⽅法中,可以看到 parameterize() ⽅法内部最终会去调⽤setParameters()⽅法,这个⽅法其实就是根据你的paramtermappings配置去将⼊参通过typehandler去精准映射, typeHandler.setParameter ⽅法就是往statment中塞参数。
public void setParameters(PreparedStatement ps){
ErrorContext.instance().activity("setting parameters").ParameterMap().getId());
List<ParameterMapping> parameterMappings = ParameterMappings();
if(parameterMappings != null){
for(int i = 0; i < parameterMappings.size(); i++){
ParameterMapping parameterMapping = (i);
Mode()!= ParameterMode.OUT){
Object value;
String propertyName = Property();
if(boundSql.hasAdditionalParameter(propertyName)){ // issue #448 ask first for additional params
value = AdditionalParameter(propertyName);
}else if(parameterObject == null){
value = null;
}else if(typeHandlerRegistry.Class())){
value = parameterObject;
}else{
MetaObject metaObject = wMetaObject(parameterObject);
value = Value(propertyName);
}
TypeHandler typeHandler = TypeHandler();
JdbcType jdbcType = JdbcType();
if(value == null && jdbcType == null){
jdbcType = JdbcTypeForNull();
}
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);
}
}
}
}
}
query()⽅法内部,其实就是调⽤execute()⽅法,然后将结果通过ResultSetHandler结果映射到反射⽣成的实体类中。
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps=(PreparedStatement) statement;
return resultSetHandler.handleResultSets(ps);
}
handleResultSets()⽅法中主要逻辑是遍历resultmap,然后将获取的结果通过 metaObject 反射赋值,
⾸先会去获取resultSet的第⼀个值,这是循环的开始,然后验证⼀下结果是否为空,便进⼊循环,每次通过getNextResultSet()⽅法获取下⼀个值,然后通过handleResultSet()去赋值,退出循环的条件就是getNextResultSet()⽅法获取不到值。
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").Id());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 从statement中获取结果
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取resultMap
List<ResultMap> resultMaps = ResultMaps();
int resultMapCount = resultMaps.size();
// 验证resultmap是否为空
validateResultMapsCount(rsw, resultMapCount);
// 循环直到所有的结果全部处理完
while(rsw != null && resultMapCount > resultSetCount){
ResultMap resultMap = (resultSetCount);
// 设值,通过metaObject
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
/
/ 解析resultSet,如果有复杂嵌套map也去解析,⼀般没有resultset,这⾥所以不⾛
String[] resultSets = ResultSets();
if(resultSets != null){
while(rsw != null && resultSetCount < resultSets.length){
ResultMapping parentMapping = (resultSets[resultSetCount]);
if(parentMapping != null){
String nestedResultMapId = NestedResultMapId();
ResultMap resultMap = ResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
resultset 遍历}
赋值是在handleResultSet()⽅法中完成的,在这个⽅法内部,最终会去执⾏handleRowValues()这个⽅法,handleRowValues()内部,有个嵌套map的逻辑分⽀,如果有复杂映射则会递归去处理,如果没有则直接调⽤handleRowValuesForSimpleResultMap()⽅法。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。