Mybatis的解析和运行原理
- mybatis解析配置文件的主要过程
- mybatis底层映射保存的数据结构(MappedStatement、SqlSource、BoundSql)
- Mybatis Mapper的运行原理
- SqlSession运行原理
- SqlSession下四大对象的设计原理和具体方法的作用
-
mybatis运行过程
- 读取mybatis配置文件配置缓存到Configuration对象,用来创建SqlSessionFactory
- SqlSession的执行过程
-
构建SqlSessionFactory的过程(Builder模式)
-
第1步:通过XMLConfigBuilder解析配置xml文件中所配置的参数,存入Configuration对象中
- Configuration采用的是单例模式
-
第2步:使用Configuration创建SqlSessionFactory。mybatis中的SqlSessionFactory是一个接口,它的实现类是DefaultSqlSessionFactory
-
XMLConfigBuilder解析xml源码
-
public class XMLConfigBuilder extends BaseBuilder{ public void parseConfiguration(XNode root){ ...... typeHandlerElement(root.evalNode("typeHandlers")); ...... } }
-
-
通过上述源码可了解XMLConfigBuilder通过一步步解析xml的内容得到对应的配置文件内容中的信息。
-
以typeHandlers配置为例说明:
-
配置的typeHandler都会被注册到typeHandlersRegistry对象中去。typeHandlersRegistry定义在XMLConfigBuilder的父类BaseBuilder中,源码如下
-
public abstract class BaseBuilder{ protected final Configuration configuration; ...... protected final TypeHandlersRegistry typeHandlersRegistry; public BaseBuilder(Configuration configuration){ this.configuration=configuration; this.typeHandlersRegistry=this.configuration.getTypeHandlersRegistry(); } ...... }
-
TypeHandlersRegistry是Configuration单例的一个属性
-
-
其他配置内容也是用类似上述的方法注册的
-
-
构建映射器的内部组成
- 当XMLConfigBuilder解析XML时,会将每一个SQL和其配置的内容保存起来,mybatis中一条sql和它相关的配置信息是由3个部分组成
- MappedStatement:保存一个映射器节点(select|insert|delete|update)
- SqlSource:是MappedStatement的一个重要属性。也是一个接口,有几个实现类。他的作用是根据上下文和参数解析生成需要的SQL,这个接口只定义了一个方法getBoundSql(paramterObject)
- BoundSql:是一个结果对象。也就是SqlSource解析到的SQL和参数,它是建立SQL和参数的地方。它有三个常用的属性
- parameterObject:参数本身,可传递对象、pojp、map、@param注解的参数
- 传递简单参数时,mybatis会把其变为父类对象传递,如int–integer
- 传递多个参数时,如果没有@param注解。mybatis会把paramObject变为Map<String,Object>对象{“1”:value,“2”:value},可以使用#{1}或#{key}取值
- 使用@param会把map的key指定为注解中的值
- parameterMappings
- 包含了元素为parameterMapping对象的List,parameterMapping描述了属性名称、表达式、javaType、jdbcType、typeHandler等信息,通过它可以实现参数与sql结合。以便PreparedStatement通过它找到parameterObject对象的属性设置参数
- sql
- 书写在映射器中的被SqlSource解析后的sql
- parameterObject:参数本身,可传递对象、pojp、map、@param注解的参数
- 当XMLConfigBuilder解析XML时,会将每一个SQL和其配置的内容保存起来,mybatis中一条sql和它相关的配置信息是由3个部分组成
-
构建SqlSessionFactory
- SqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
- 先根据文件流生成Configuration对象,进而构建SqlSessionFactory对象
-
SqlSession运行过程
-
映射器Mapper的动态代理
-
xxxMapper xxmappner=sqlSession.getMapper(xxxMapper.class); //getMapper-->SqlSession源码 public class DefaultSqlSession implements SqlSession{ @Override public <T> T getMapper(Class<T> type){ //调用configuration的getMapper return configuration.<T>getMapper(type,this); } }
-
-
```java
//configuration
public <T> T getMapper(Class<T> type,SqlSession sqlSession){
//运用映射器的注册器mapperRegistry来获取对应的接口对象
return mapperRegistry.getMapper(type,sqlSession);
}
```
```java
//MapperRegistry源码片段
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
}
/**
*判断是否注册一个Mapper,如果没有则会抛出异常,如果有,就会启用MapperProxyFactory工厂来生成一个代理实例
*/
```
```java
//MapperProxyFactory生成代理对象
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
//生成代理对象并调用invoke代理方法
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
```
```java
//调用代理方法源码
public class MapperProxy<T> implements InvocationHandler, Serializable {
......
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
......
}
/**
*如果Mapper是一个jdk动态代理对象,那么它会运行到invoke,invoke首先判断是否是一个类,这里的Mapper是一个接口不是一个类,所以会通过cachedMapperMethod生成MapperMethod对象,最后执行execute方法,把SqlSession和当前运行的参数传递进去
*/
```
```java
public class MapperMethod {
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
//查询
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
Object param = this.method.convertArgsToSqlCommandParam(args);
List result;
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectList(this.command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(this.command.getName(), param);
}
if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
} else {
return result;
}
}
}
```
- **SqlSession下的四大对象**
- 映射器就是一个动态代理对象进入到了MapperMethod的execute方法,然后经过简单的判断就进入了SqlSession的crud等方法,下面介绍这些方法是如何执行的,这是正确编写插件的根本
- Executor:执行器,负责调度**StatementHandler**、ParameterHandler、ResultSetHandler来执行对应的SQL
- StatementHandler:负责使用数据库的Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现。
- ParameterHandler:用来处理SQL语句
- ResultSetHandler:进行数据集(ResultSet)的封装返回处理
- Executor
- 执行器:执行java和数据库交互的对象,可以在settings元素中的defaultExecutorType中设置3种执行器
- 先看看mybatis是如何创建Executor的
- ```java
//Configuration.newExecutor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
//mybatis的插件、构建一层层动态代理对象
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
```
```java
//以Simple执行器为例
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
//把ResultHandler传递进去,用来组织结果返回给调用者来完成一次查询
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
//预编译及基础设置
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
//设置参数
handler.parameterize(stmt);
return stmt;
}
}
/**
*mybatis根据Config来构建StatementHandler,然后使用prepareStatement对sql和参数进行初始化
*/
```
- StatementHandler——数据库会话器
- 用来处理数据库会话
- ```java
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 = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
```
创建的真实对象是RoutingStatementHandler,和Executor一样,用代理对象做一层层封装
RoutingStatementHandler不是真实的服务对象,它是通过适配模式来找到对应的statementHandler来执行。RoutingStatementHandler分为三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。它对应的是jdbc的Statement、PreparedStatement(预编译处理)、CallableStatement(存储过程处理)。
初始化RoutingStatementHandler对象时,会根据上下文环境决定创建哪个具体的StatementHandeler对象实例
```java
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch(ms.getStatementType()) {
case STATEMENT:
this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
```
以PreparedStatementHandler为例。Executor执行查询时会执行StatementHandler的prepare、parameterize、query方法
```java
public abstract class BaseStatementHandler implements StatementHandler {
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(this.boundSql.getSql());
Statement statement = null;
try {
//对sql进行预编译和基础配置
statement = this.instantiateStatement(connection);
this.setStatementTimeout(statement, transactionTimeout);
this.setFetchSize(statement);
return statement;
} catch (SQLException var5) {
this.closeStatement(statement);
throw var5;
} catch (Exception var6) {
this.closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + var6, var6);
}
}
}
```
```java
//设置参数
public class PreparedStatementHandler extends BaseStatementHandler {
public void parameterize(Statement statement) throws SQLException {
this.parameterHandler.setParameters((PreparedStatement)statement);
}
}
```
```java
//查询方法
public class PreparedStatementHandler extends BaseStatementHandler {
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return this.resultSetHandler.handleResultSets(ps);
}
}
```
- 一条SQL的执行过程:Executor先调用StatementHandler的prepare()方法预编译SQL,同时设置一些基本运行的参数。然后用parameterize()方法启用ParameterHandler设置参数,完成预编译,执行查询。如果是查询,mybatis会使用ResultSetHandler封装结果返回给调用者。
- ParameterHandler——参数处理器
- 它的作用是完成对预编译参数的设置
- ```java
public interface ParameterHandler {
//返回参数对象
Object getParameterObject();
//设置预编译sql语句的参数
void setParameters(PreparedStatement var1) throws SQLException;
}
```
```java
//实现类
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
......
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
if (parameterMappings != null) {
for(int i = 0; i < parameterMappings.size(); ++i) {
ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
if (this.boundSql.hasAdditionalParameter(propertyName)) {
value = this.boundSql.getAdditionalParameter(propertyName);
} else if (this.parameterObject == null) {
value = null;
} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
value = this.parameterObject;
} else {
MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = this.configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
} catch (SQLException var11) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11);
}
}
}
}
}
}
```
- 从paramterObject对象中取到参数,然后使用typeHandler转换参数,如果有设置,那么它会根据签名注册的typeHandler对参数进行处理。而typeHandler也是再mybatis初始化时,注册在Configuration中的
- ResultSetHandler——结果处理器
- 用来组装结果返回
- ```java
public interface ResultSetHandler {
//用来包装结果集
<E> List<E> handleResultSets(Statement var1) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
//处理存储过程输出参数
void handleOutputParameters(CallableStatement var1) throws SQLException;
}
```
- ```java
public class DefaultResultSetHandler implements ResultSetHandler {
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
List<Object> multipleResults = new ArrayList();
int resultSetCount = 0;
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
this.validateResultMapsCount(rsw, resultMapCount);
while(rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
String[] resultSets = this.mappedStatement.getResultSets();
if (resultSets != null) {
while(rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
}
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
}
return this.collapseSingleResultList(multipleResults);
}
}
```