1.模板模式
Tempalte Pattern:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需重写方法实现,但调用将以抽象类中定义的方式进行。
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定实现。
2.Executor组件分析
Executor是MyBatis核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的。
- SimpleExecutor:默认配置,使用PrepareStatement对象访问数据库,每次访问都要创建新的PrepareStatement对象;
ReuseExecutor:使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象;
BatchExecutor:实现批量执行多条SQL语句的能力。
2.1 BaseExecutor模板模式应用
BaseExecutor抽象类实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate、doQuery等方法。
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取sql语句信息,包括占位符,参数等信息
BoundSql boundSql = ms.getBoundSql(parameter);
//拼装缓存的key值
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {//检查当前executor是否关闭
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {//非嵌套查询,并且FlushCache配置为true,则需要清空一级缓存
clearLocalCache();
}
List<E> list;
try {
queryStack++;//查询层次加一
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//查询以及缓存
if (list != null) {
//针对调用存储过程的结果处理
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//缓存未命中,从数据库加载数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {//延迟加载处理
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {//如果当前sql的一级缓存配置为STATEMENT,查询完既清空一集缓存
// issue #482
clearLocalCache();
}
}
return list;
}
真正查询数据库:
//真正访问数据库获取结果的方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);//在缓存中添加占位符
try {
//调用抽象方法doQuery,方法查询数据库并返回结果,可选的实现包括:simple、reuse、batch
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);//在缓存中删除占位符
}
localCache.putObject(key, list);//将真正的结果对象添加到一级缓存
if (ms.getStatementType() == StatementType.CALLABLE) {//如果是调用存储过程
localOutputParameterCache.putObject(key, parameter);//缓存输出类型结果参数
}
return list;
}
其中doQuery、doUpdate是抽象方法,需要子类实现。
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
2.2 SimpleExecutor的doQuery实现
@Override
//查询的实现
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();//获取configuration对象
//创建StatementHandler对象,
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
2.3 ReuseExecutor的doQuery
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();//获取configuration对象
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
}
ReuseExecutor与SimpleExecutor不同的是prepareStatement中获取语句时,多了一步缓存存取:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();//获取sql语句
if (hasStatementFor(sql)) {//根据sql语句检查是否缓存了对应的Statement
stmt = getStatement(sql);//获取缓存的Statement
applyTransactionTimeout(stmt);//设置新的超时时间
} else {//缓存中没有statment,创建statment过程和SimpleExecutor类似
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);//放入缓存中
}
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
2.4 二级缓存
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);
}
//如果有<cache>节点,通过装饰器,添加二级缓存的能力
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//通过interceptorChain遍历所有的插件为executor增强,添加插件的功能
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
CachingExecutor的query:
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//从MappedStatement中获取二级缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);//从二级缓存中获取数据
if (list == null) {
//二级缓存为空,才会调用BaseExecutor.query
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这就是先查询二级缓存,再走一级缓存的原因(装饰器)。
3.Executor内部运作过程——StatementHandler分析
通过对SimpleExecutor doQuery()方法的解读,可知Executor调度三个小弟协同完成工作:
- StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作。起承上启下作用;
- ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的占位符“?”都对应BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性;
- ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型。
3.1 StatementHandler分析
- StatementHandler完成MyBatis最核心的工作,也是Executor实现的基础。功能包括:创建statement对象,为sql语句绑定参数,执行增删改查等SQL语句,将结果集映射进行转化。
- BaseStatementHandler:所有子类的抽象父类,定义了初始化statement的操作顺序,由子类实现具体的实例化不同的statement(模板模式)
- RoutingStatementHandler:Executor组件真正实例化的子类,使用静态代理模式,根据上下文决定创建哪个具体实体类。
- SimpleStatementHandler:使用statement对象访问数据库,无须参数化
- PreparedStatementHandler:使用预编译PreparedStatement对象访问数据库
- CallableStatementHandler:调用存储过程
3.1.1 BaseStatementHandler
BaseStatementHandler定义了模板方法prepare,其中instantiateStatement是抽象方法,由具体子类的实现。
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
//结果处理器,对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;
protected final ResultSetHandler resultSetHandler;
//sql占位符处理器,对预编译的SQL语句进行参数设置
protected final ParameterHandler parameterHandler;
//记录执行语句的executor对象
protected final Executor executor;
//sql语句对应的MappedStatement
protected final MappedStatement mappedStatement;
//分页信息
protected final RowBounds rowBounds;
//sql语句
protected BoundSql boundSql;
@Override
//使用模板模式,定义了获取Statement的步骤,其子类实现实例化Statement的具体的方式;
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//通过不同的子类实例化不同的Statement,分为三类:simple(statment)、prepare(prepareStatement)、callable(CallableStatementHandler)
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);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
PreparedStatementHandler的instantiateStatement:
@Override
//使用底层的prepareStatement对象来完成对数据库的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
//根据mappedStatement.getKeyGenerator字段,创建prepareStatement
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {//对于insert语句
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
//返回数据库生成的主键
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
//返回数据库生成的主键填充至keyColumnNames中指定的列
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
//设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//创建普通的prepareStatement对象
return connection.prepareStatement(sql);
}
}
SimpleStatementHandler的instantiateStatement:
@Override
//使用底层的statment对象来完成对数据库的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
//设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
3.1.2 RoutingStatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建RoutingStatementHandler对象,实际由statmentType来指定真实的StatementHandler来实现
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
使用静态代理模式,根据上下文决定创建哪个具体实体类:
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler最主要的功能就是根据mappedStatment的配置,生成一个对应的StatementHandler对象并赋值给delegate
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());
}
}
3.2 ParameterHandler
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
调用PreparedStatementHandler的parameterize方法(SimpleStatementHandler的parameterize方法为空):
@Override
//使用parameterHandler对sql语句的占位符进行处理
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
调用DefaultParameterHandler:
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql中获取sql语句的占位符对应的参数信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//遍历这个参数列表,把参数设置到PreparedStatement中
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)) { // 获取对应的实参值
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();//从parameterMapping中获取typeHandler对象
JdbcType jdbcType = parameterMapping.getJdbcType();//获取参数对应的jdbcType
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//为statment中的占位符绑定参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
3.3 ResultSetHandler
//查询的实现
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();//获取configuration对象
//创建StatementHandler对象,
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
最后一步调用PreparedStatementHandler的query方法:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
调用DefaultResultSetHandler的handleResultSets方法:
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//用于保存结果集对象
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//statment可能返回多个结果集对象,这里先取出第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);//结果集和resultMap不能为空,为空抛出异常
while (rsw != null && resultMapCount > resultSetCount) {
//获取当前结果集对应的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);//获取下一个结果集
cleanUpAfterHandlingResultSet();//清空nestedResultObjects对象
resultSetCount++;
}
//获取多结果集。多结果集一般出现在存储过程的执行,存储过程返回多个resultset,
//mappedStatement.resultSets属性列出多个结果集的名称,用逗号分割;
//多结果集的处理不是重点,暂时不分析
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
4.总结
- 1)StatementHandler完成的工作就是JDBC完成的工作
尤其是:prepareStatement->设置参数->查询->查询结果映射到POJO。
@Test
public void QueryPreparedStatementDemo() {
Connection conn = null;
PreparedStatement stmt = null;
List<TUser> users = new ArrayList<>();
try {
// STEP 2: 注册mysql的驱动
Class.forName("com.mysql.jdbc.Driver");
// STEP 3: 获得一个连接
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// STEP 4: 创建一个查询
System.out.println("Creating statement...");
String sql;
sql = "SELECT * FROM t_user where user_name= ? ";
stmt = conn.prepareStatement(sql);
stmt.setString(1, "lison");
System.out.println(stmt.toString());//打印sql
ResultSet rs = stmt.executeQuery();
// STEP 5: 从resultSet中获取数据并转化成bean
while (rs.next()) {
System.out.println("------------------------------");
// Retrieve by column name
TUser user = new TUser();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setRealName(rs.getString("real_name"));
user.setSex(rs.getByte("sex"));
user.setMobile(rs.getString("mobile"));
user.setEmail(rs.getString("email"));
user.setNote(rs.getString("note"));
System.out.println(user.toString());
users.add(user);
}
// STEP 6: 关闭连接
rs.close();
stmt.close();
conn.close();
- 2)ResultSetHandler关键的步骤
跟mybatis基础层源码分析4-反射模块中最后模拟代码核心步骤是一样的。
@Test
public void reflectionTest(){
//反射工具类初始化
ObjectFactory objectFactory = new DefaultObjectFactory();
TUser user = objectFactory.create(TUser.class);
ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
MetaObject metaObject = MetaObject.forObject(user, objectFactory, objectWrapperFactory, reflectorFactory);
//模拟数据库行数据转化成对象
//1.模拟从数据库读取数据
Map<String, Object> dbResult = new HashMap<>();
dbResult.put("id", 1);
dbResult.put("user_name", "lison");
dbResult.put("real_name", "李晓宇");
TPosition tp = new TPosition();
tp.setId(1);
dbResult.put("position_id", tp);
//2.模拟映射关系
Map<String, String> mapper = new HashMap<String, String>();
mapper.put("id", "id");
mapper.put("userName", "user_name");
mapper.put("realName", "real_name");
mapper.put("position", "position_id");
//3.使用反射工具类将行数据转换成pojo
BeanWrapper objectWrapper = (BeanWrapper) metaObject.getObjectWrapper();
Set<Map.Entry<String, String>> entrySet = mapper.entrySet();
for (Map.Entry<String, String> colInfo : entrySet) {
String propName = colInfo.getKey();
Object propValue = dbResult.get(colInfo.getValue());
PropertyTokenizer proTokenizer = new PropertyTokenizer(propName);
objectWrapper.set(proTokenizer, propValue);
}
System.out.println(metaObject.getOriginalObject());
}
参考
- 1)享学课堂Lison老师笔记