Student studentSelect = new Student();
studentSelect.setStudentId(new Long(1));
List<Student> students = studentMapper.selectWithCondition(studentSelect);
相关的mapper.java和mapper.xml代码如下:
package com.fengxing.mapper;
import java.util.List;
import com.fengxing.dto.Student;
public interface StudentMapper {
public List<Student> selectWithCondition(Student student);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fengxing.mapper.StudentMapper" >
<select id="selectWithCondition" resultType="com.fengxing.dto.Student"
parameterType="com.fengxing.dto.Student">
select *
from student stu
<where>
<if test="studentId!=null">
stu.student_id = #{studentId}
</if>
<if test="major!=null">
stu.major = #{major}
</if>
<if test="name!=null">
stu.name = #{name}
</if>
</where>
</select>
</mapper>
整个查询的时序图如下:
下面对这些步骤进行详细讲解:
(一)MapperProxy和MappedMethod
在调用studentMapper.selectWithCondition(studentSelect)的时候,studentMapper只是个接口,我们并没有具体去实现这个接口,这个mybatis使用了动态代理,真正执行的是MapperProxy的invoke方法。
mapper代理对象的创建通过MapperProxyFactory创建,具体方法如下,其中mapperProxy是一个实现了InvocationHandler的对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
MapperProxy的部分方法如下:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
invoke方法中先判断传入的方法是不是Object中声明的方法,是的话直接invoke放过
否者先获取MapperMethod,然后调用MapperMethod的execute方法。MapperMethod中保存了查询方法的类型,名称,返回类型等详细信息
MapperMethod的execute(SqlSession sqlSession, Object[] args)方法如下,根据增删改查的类型以及返回值的类型执行不同的方法,这里将调用executeForMany(sqlSession, args)
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
MapperMethod的executeForMany方法:
里面调用sqlSession的selectList方法
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
(二)SqlSession
MapperMethod的executeForMany方法中调用sqlSession的selectList方法,这里的sqlSession是sqlSessionTemplate,具体会调用代理对象sqlSessionProxy的selectList,然后到invoke方法,invoke方法中创建具体的DefaultSqlSession,然后执行selectList,这个过程还会创建四大对象之一的executor,具体过程可以看mybatis(一)
DefaultSqlSession的selectList方法如下:
@Override
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);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
先获取了MappedStatement对象,MappedStatement对象的信息如下,主要记录了mapper.xml文件中select标签的各种属性值
接着开始调用Executor的query方法
(三)Executor
Executor是一个接口,BaseExecutor和CachingExecutor都实现了Executor,其中BaseExecutor是抽象类,SimpleExecutor又继承并实现了BaseExecutor
这里先进入CachingExecutor的4个参数的query方法,这个方法有可能被拦截器拦截,比如pageHelper就会拦截这个方法,该方法如下:
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这个方法会创建 boundSql,boundSql的属性如下,有sql语句,查询参数,已经查询参数的类型等 ,可以看到boundSql已经没有了mybatis拼接sql的各种标签了(这里的sql和mapper.xml中写的不一样,原因是我自己定义了拦截器,对sql做了更改)
所以BoundSql boundSql = ms.getBoundSql(parameterObject)这行代码是做sql语句的拼接
接着调用CachingExecutor自身的5个参数的query方法,
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
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);
}
这个查询方法中有对二级缓存的处理,如果没有从缓存中拿到,就调用delegate的query,这里delegate是SimpleExecutor,由于SimpleExecutor继承于BaseExecutor,这个方法是BaseExecutor实现的,所以先到BaseExecutor
BaseExecutor的query方法如下:
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) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
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) {
// issue #482
clearLocalCache();
}
}
return list;
}
这个方法体现了一级缓存,一级缓存没有命中的话,就调用queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql)查询数据库。
BaseExecutor的queryFromDataBase方法:
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 {
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;
}
查询数据库前先在localCacha中放一个占位符,查询完成后,先清除之前的缓存,然后将查到的数据放入缓存中
查询的doQuery方法BaseExecutor没有实现,是由SimpleExecutor实现的,所以又到了SimpleExecutor的doQuery方法
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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
这个方法包括三个步骤
1.创建四大对象之一的StatementHandler
2.利用StatementHandler获取JDBC原生的Statement
3.利用1,2获取的对象执行查询操作
(四)除Executor之外的其余三大对象的创建以及其中的查询处理流程
configuration中newStatementHandler方法如下:
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;
}
先创建一个RoutingStatementHandler,然后包装成拦截器链。
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());
}
}
根据statementType的不同,分别创建SimpleStatementHandler, PreparedStatementHandler和CallableStatementHandler,依次对应了jdbc的三种statement,statementType是在mapper.xml文件中的增删改查标签中定义的,默认是PREPARED
PreparedStatementHandler继承自PreparedStatementHandler,在BaseStatementHandler的构造方法中创建了四大对象的其余两个,即ParameterHandler和ResultSetHandler
PreparedStatementHandler和PreparedStatementHandler的构造方法如下:
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
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);
}
StatementHandler创建完成后,开始创建JDBC原生的Statement
SimpleExecutor中的prepareStatement方法:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
stmt = handler.prepare(connection, transaction.getTimeout());是预编译sql生成Statement对象
handler.parameterize(stmt);是设置预编译参数
第三步就是执行查询操作
最终会调用PrepareStatementHandler的query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
这里包括两个步骤
1. 调用PrepareStatement的excute方法查询数据库
2.利用ResultSetHandler对象对结果进行处理和封装
阅读更多
---------------------
作者:科西嘉狮子
来源:CSDN
原文:https://blog.csdn.net/qq_38478903/article/details/84928862
版权声明:本文为博主原创文章,转载请附上博文链接!