思考
BaseExecutor清空缓存,怎样才算是一个Executor,一个SqlSession一个Executor,spring里SqlSession什么时候创建?-
每个mapperInterface对应一个MapperProxyFactory,创造出的MapperProxy都会共用methodCache缓存,
而每个MapperProxy绑定一个SqlSession,每`getMapper(Class<T> type, SqlSession sqlSession)`一下就创建出一个MapperProxy,这样设计原因?-- 没有整合spring,就这样设计,实际的单例管理交给spring
一、二级缓存区别? ok
@CacheNamespace @CacheNamespaceRef使用
JDBC源码,Prepared 如何防止参数注入攻击
访问者模式
TransactionSynchronizationManager
SpringManagedTransaction sping事务管理
ResultSetHandler
ParameterHandler
DataSource SqlSession Connection 的生命周期 -
mybatis架构图:

1. mybatis调用栈
以selectByExample(example)为例,看下mybatis”主线任务”

-
首先JDK动态代理类调用 MapperProxy(实现InvocationHandler接口)的invoke方法
-
然后调用MapperMethod的
Object execute(SqlSession sqlSession, Object[] args),之后走SqlSession对应方法List<E> selectList(String statement, Object parameter) - DefaultSqlSession会从全局配置对象中找到对应的MappedStatement对象,然后给Executor接口执行query()
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } ... } - Executor 这层使用了SimpleExecutor,用CacheExecutor装饰,没有配置二级缓存直接走抽象类BaseExecutor
List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)BaseExecutor做一级缓存,然后调用SimpleExecutor的doQuery(),少了CacheKey参数,
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //创建出相应的StatementHandler StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //会调用handler.prepare() handler.parameterize() stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } - StatementHandler Executor执行doQuery()的时候已经创建出对应的StatementHandler,由RoutingStatementHandler静态代理, 这里由参数 用PreparedStatementHandler,最后调用jdbc的java.sql.PreparedStatement执行sql语句
2. SqlSession
SqlSession主要作用
-
选择一个Executor策略(SIMPLE, REUSE, BATCH),将Executor封装,提供各种入参和返回值的方法
-
管理Session,SqlSessionManager提供一个线程绑定一个SqlSession,避免多次创建
DefaultSqlSession
private final Configuration configuration;
private final Executor executor; //sql执行的Executor对象,策略模式,可选择SIMPLE, REUSE, BATCH
private final boolean autoCommit; //是否自动提交事务
private boolean dirty; //缓存中是否有脏数据
private List<Cursor<?>> cursorList; //游标,防止用户未关闭
DefaultSqlSession各个方法调用关系,最终会走Executor的方法

DefaultSqlSessionFactory
主要提供了两种创建DefaultSqlSession 对象的方式
SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
SqlSession openSessionFromConnection(ExecutorType execType, Connection connection)
SqlSessionManager
同时实现了SqlSession 接口和SqlSessionFactory
//底层封装的SqlSessionFactory 对象
private final SqlSessionFactory sqlSessionFactory;
//ThreadLocal 变量,记录一个与当前线希呈绑定的SqlSession 对象
private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
//localSqlSession 中记录的SqlSession 对象的代理对象,在SqlSessionManager初始化时,
//会使用JDK动态代理的方式为localSqlSession建代理对象
private final SqlSession sqlSessionProxy;
3. Executor
Executor 接口,中间做一堆优化操作,最终调用StatementHandler
BaseExecutor 模板方法的抽象类,封装了结果集的一级缓存,实现类三个:
1 SimpleExecutor 简单调用,每次创建都Statement
2 ReuseExecutor 缓存Statement, Map<String, Statement> statementMap字段
3 BatchExecutor 批处理sql,缓存部分sql一起发送到服务器,减少一次sql一次发送的消耗
CacheExecutor 二级缓存,装饰者,为BaseExecutor提供二级缓存
SimpleExecutor的doQuery实现:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//创建出相应的StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//会调用transaction.getConnection() handler.prepare() handler.parameterize()
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
4. StatementHandler
StatementHandler接口及其实现类:

public interface StatementHandler {
// 一般首先调用,得到Statement,供后续方法
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
// 绑定参数
void parameterize(Statement statement) throws SQLException;
// 下面四个方法都是具体的数据库执行
void batch(Statement statement) throws SQLException;
int update(Statement statement) throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(Statement statement) throws SQLException;
BoundSql getBoundSql(); // 绑定的sql
ParameterHandler getParameterHandler(); // 参数处理器
}
1. RoutingStatementHandler
如名字,路由作用,对不同StatementType创建不同的策略Handler
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());
}
}
2. BaseStatementHandler
抽象类,主要提供参数绑定和 实现prepare()方法
Executor调用逻辑:先调用BaseStatement的prepare(),prepare()调用instantiateStatement()得到Statement,再调用parameterize,
最后拿Statement调query(),最后用resultSetHandler处理结果
BaseStatementHandler主要的实现:
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
//ResultSetHandler 将结采集映射成结采对象
protected final ResultSetHandler resultSetHandler;
//为SQL 语句绑定实参,使用传入的实参替换SQL语句的中"?"占位符
protected final ParameterHandler parameterHandler;
//记录执行SQL 语句的Executor 对象
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
//RowBounds 记录了用户设置的offset 和limit ,用于在结采集中定位映射的起始位置和结束位置
protected BoundSql boundSql;
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//instantiateStatement抽象方法,字类实现
statement = instantiateStatement(connection);
//配置超时时间以 fetchSize
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch ...
}
3. SimpleStatementHandler
SimpleStatementHandler基于java.sql.Statement接口,执行不带参数的sql语句(带参数需要自己拼装sql,有sql注入问题),主要的实现:
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
//创建普通的Statement对象
return connection.createStatement();
} else {
//设置结果集是否可以滚动及其游标是否可以上下移动,设置结果集是否可更新
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
@Override
//空实现
public void parameterize(Statement statement) {
// N/A
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
4.PreparedStatementHandler
PreparedStatementHandler基于java.sql.PreparedStatement,可以绑定参数接口主要实现:
@Override
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 {
//在insert 语句执行完成之后,会将keyColumnNames 指定的列返回
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
//创建普通的PreparedStatement 对象
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
//使用parameterHandler来绑定参数
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
通过比较PreparedStatementHandler在创建Statement时绑定sqlconnection.prepareStatement(sql),执行直接ps.execute()
而SimpleStatementHandler:connection.createStatement(),statement.execute(sql)
“Prepared”可以理解为准备sql,每次execute的sql一样参数可以不一样,而Simple每次execute的sql不一样
5. CallableStatementHandler
存储过程的调用,基于java.sql.CallableStatement,也会调用ParameterHandler.setParameters() 方法完成SQL语句的参数绑定,
还会调用ResultSetHandler.handleOutputParameters()处理输出参数
//差不多
@Override
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);
}
}
@Override
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());
}
}
}
}
}
}
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;
}