一、Mybatis的工作原理
1.构建 mybatis 配置文件
构建 mybatis 的config,xml,java的mapper,entity类
config文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=utf8&useSSL=false"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="org/mybatis/example/BlogMapper.xml"/></mappers>
</configuration>
xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.afu.dao.EmpDao"><select id="selectAll" resultType="com.afu.entity.Emp">SELECT * FROM `emp`</select>
</mapper>
entity类
@Data
public class Emp {private Integer empno;private String ename;private String job;
}
mapper 类
public interface EmpDao {List<Emp> selectAll();
}
2.加载mybaitis 配置文件
我们获取配置文件的io流,然后SqlSessionFactotyBuilder通过加载io流来生成SqlSessionFactory对象
InputStream is = Resources.getResourceAsStream("mybatis-conf.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSessionFactoryBuilder 的build方法,所有build,最终调用build(InputStream inputStream, String environment, Properties properties)
public SqlSessionFactory build(InputStream inputStream) {return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
SqlSessionFactotyBuilder的builder的方法,加载所config文件的信息,最后生成一个Configuration对象然后传入SqlSessionFactory
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {SqlSessionFactory var5;try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);var5 = this.build(parser.parse());} catch (Exception var14) {throw ExceptionFactory.wrapException("Error building SqlSession.", var14);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException var13) {}}return var5;
}
3.生成sqlSession 对象
通过sqlSessionFactory openSession方法获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession openSession会调用openSessionFromDataSource方法
public SqlSession openSession() {return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
获取环境配置类,获取transactionFactory事务工厂类,通过连接获取事务类,然后获取executor对象,最后创建一个sqlsession对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;DefaultSqlSession var8;try {Environment environment = this.configuration.getEnvironment();TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);Executor executor = this.configuration.newExecutor(tx, execType);var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);} catch (Exception var12) {this.closeTransaction(tx);throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);} finally {ErrorContext.instance().reset();}return var8;
}
4.生成Mapper 代理对象
mybatis 会通过Configuration类中获取,最后通过mybatis 的动态代理生成
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
public <T> T getMapper(Class<T> type) {return this.configuration.getMapper(type, this);
}
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);}}
}
5.调用的mapper的方法
由于mapper 的实现类,是由 Java的动态代理实现,再调用方法,实际执行的是代理类的增强方法mybaits 类中MapperProxy的invoke方法, 下面我们看看invoke方法
mapper.selectAll();
判断是否执行的是object 的方法,如果是Object 的方法就执行原生态的方法,或者执行mybaits代理方法invoke 默认执行的是PlainMethodInvoker类的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}
}
执行MapperMethod mapperMethod类的
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);}
execute 方法,会根据执行方法的类型,执行sqlsession的不同方法,他们的流程都是首先获取参数列表,然后执行sqlSession对应的方法,在这里我就不一一讲解了,在这里我来讲解 result = 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);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}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;
}
executeForMany方法
首先解析方法参数列表
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.selectList(command.getName(), param, rowBounds);} else {result = sqlSession.selectList(command.getName(), param);}// issue #510 Collections & arrays supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result;
}
convertArgsToSqlCommandParam 会执行
public Object convertArgsToSqlCommandParam(Object[] args) {return paramNameResolver.getNamedParams(args);
}
getNamedParams(args)方法体内容,getNamedParams方法首先获取方法数量,如果方法数量为0,或者参数为null:直接返回null,
如果参数只有一个就会根据他的类型(集合,数组),如果是集合返回ParamMap,否则直接把自己给返回
如果有多个参数,会根据你设置 @Param的名字进行设置,如果没有设置,它会按param+ 下标进行设置
public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {Object value = args[names.firstKey()];return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);} else {final Map<String, Object> param = new ParamMap<>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);// ensure not to overwrite parameter named with @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}
}
会判断当前是否分页来执行sql 语句,会执行selectList方法
public <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
执行CachingExecutor 的query方法
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, handler);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}
CachingExecutor ,获取BoundSql对象,创建缓存, 执行query方法
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);
}
query 首先到缓存中去拿值,拿不到,就执行SimpleExecutor 的query方法
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, boundSql);@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
到缓存里取,取到了就用缓存的数据,否则就到数据库进行查询数据
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 #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;
}
通过doQuery 进行查询数据,然后讲缓存存到数据库
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;
}
通过configuration创建StatementHandler,parameterHandler,resultSetHandler
然后执行他们的pluginAll方法,插件方法
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.query(stmt, resultHandler);} finally {closeStatement(stmt);}
}
在进行执行 prepareStatement方法中执行parameterHandler的parameterize方法
@Override
public void parameterize(Statement statement) throws SQLException {delegate.parameterize(statement);
}public void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);
}
最后来到setParameters方法,讲参数进行解析,然后进行赋值
public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();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)) { // issue #448 ask first for additional paramsvalue = 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();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}
}
handler.query(stmt, resultHandler);
PreparedStatement ps = (PreparedStatement) statement;ps.execute();rhreturn resultSetHandler.handleResultSets(ps);
通过resultSetHandler进行对返回值进行解析
final List<Object> multipleResults = new ArrayList<>();int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {ResultMap resultMap = resultMaps.get(resultSetCount);handleResultSet(rsw, resultMap, multipleResults, null);rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;
}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);
最后我们将返回值进行返回