文章目录
摘要
近期由于项目使用mybatis出现了数据源阻塞,导致应用程序假死,服务超时引发严重后果,故此下定决心重新梳理一下spring+mybatis+c3p0整合问题,主要分为:配置、源码(通过一次数据库操作分析)、myabatis缓存、问题总结
spring + mybatis + c3p0 整合(配置篇)
spring + mybatis + c3p0 整合(源码分析)
mybatis整体架构
mybatis整体架构分为三层:基础支持层、核心处理层、接口层
接口层
接口层是上层应用于mybatis交互的桥梁,核心是SqlSession接口,暴露了一系列正删改查API给应用程序;接口层接受到请求会调用核心处理层完成具体数据库操作
SqlSession接口实现使用了工厂方法模式,SqlSessionFactory负责创建SqlSession对象,包含了多个openSession方法的重载
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
核心处理层
核心处理层完成核心处理流程,包括mybatis初始化和数据库操作所涉及的全部流程;如参数映射、sql解析、sql执行、结果处理和映射;接下来主要分析这一层的相关对象。
基础支持层
如事务管理、数据源、缓存管理、反射模块、日志、解析器模块、类型转换、Binding模块、资源加载等;后面我们主要分析下缓存管理,其他的有兴趣的可自行学习
mybatis核心对象
- Configuration 核心环境配置,配置文件映射关系等都会保存在这里,mybatis四大核心组件都是在configuration中创建。相关源码如下
- Executor 一个 SqlSession 对应一个 Executor 对象,这个对象负责增删改查的具体操作。
- ParameterHandler 参数处理器, 没有过多的类关联关系, 只有一个默认的实现类。
- StatementHandler 是 mybatis 创建 Statement 的处理器, 会负责 Statement 的创建工作, 在 JDBC 中 Statement 执行 SQL 语句时主要分为两个主要对象Statement 和 PrepareStatement。
- ResultSetHandler 结果处理器,与ParameterHandler 类似只有一个默认实现类。
附上一张核心对象的调用关系
configuration创建4大核心对象源码
上篇有提过Configuration对象是SqlSessionFactoryBean.buildSqlSessionFactory()方法创建的,若未配置mybatis-config.xml默认new一个Configuration对象;若指定了配置文件configLocation会通过XMLConfigBuilder解析配置文件设置相关熟悉创建Configuration对象
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
Executor组件
这里用了模板设计模式和装饰器模式
- BaseExecutor提供默认实现,并提供抽象方法由不同实现类负责实现不同功能
- CachingExecutor实现缓存管理;优先从缓存查询,如果存在就返回;如果不存在就交由BaseExecutor的子实现类处理
- SimpleExecuto默认使用的执行器, 每执行一次 update 或 select, 就开启一个 Statement 对象, 用完就直接关闭 Statement 对象
- ReuseExecutor可重用的执行器, 可重用的对象为 Statement, 在执行器内部提供一个 Map 映射, 执行 SQL 作为 key, value 为 Statement
- BatchExecutor 批量提交 SQL 的一个执行器, 用作批量提交;可解决foreach拼接sql过长问题
StatementHandler组件
看类图就明白,其设计与Executor类似;其作用是创建执行sql的Statement
JDBC中 Statement 执行 SQL 语句时主要分为两个主要对象:Statement 和 PrepareStatement
- BaseStatementHandler提供模板代码,具体创建Statement类型通过抽象方法instantiateStatement(),不同子类做不同实现
- SimpleStatementHandler,负责处理不带动态参数运行的 SQL,对应Statement
- PreparedStatementHandler, 负责处理 SQL 中的占位符动态参数赋值操作,对应PrepareStatement ;能够避免sql注入问题
- CallableStatementHandler,复制处理存储过程的SQL,对应CallableStatement
- RoutingStatementHandler看名称就知道这是个路由对象,根据不同statementType 决定具体实现
BaseStatementHandler相关源码
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);
}
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement);
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);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
RoutingStatementHandler部分源码
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());
}
}
ParameterHandler组件 参数处理器
主要作用于 PreparedStatemet 参数解析时使用;从上文BaseStatementHandler源码可知其在初始化StatementHandler组件时创建
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
ResultSetHandler 组件 结果处理器
同参数处理器类型,负责处理 Statement 返回的结果, 根据定义返回类型进行封装返回;从上文BaseStatementHandler源码可知其在初始化StatementHandler组件时创建
插件开发
上文Configuration源码可以看出,mybatis四大核心对象都支持插件化的,其使用的是责任链设计模式;有兴趣的可以自行研究探讨,这里不做详细分析了