工作流程:
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserByUsername("admin");
}
1、获取配置文件mybatis-config.xml,由SqlSessionFactoryBuilder对象,读取并解析配置文件,返回SqlSessionFactory对象
2、由SqlSessionFactory创建SqlSession 对象,没有手动设置的话事务默认开启
3、调用SqlSession中的api,获取指定UserMapper.class文件
4、由jdk动态代理,获取到UserMapper.xml文件,内部进行复杂的处理,最后调用jdbc执行SQL语句,封装结果返回。
具体实现:
1.1、由SqlSessionFactoryBuilder读取xml配置文件,解析成Configuration对象
//在创建XMLConfigBuilder时,它的构造方法中解析器XPathParser已经读取了配置文件
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签
parseConfiguration(parser.evalNode("/configuration"));
//最后返回的是Configuration 对象
return configuration;
}
//此方法中读取了各个标签内容并封装到Configuration中的属性中。
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
1.2、根据Configuration创建SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
//创建了DefaultSqlSessionFactory对象,传入Configuration对象。
return new DefaultSqlSessionFactory(config);
}
2.1、根据SqlSessionFactory创建会话DefaultSqlSession对象
// 创建SqlSession会话
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
/**
*
* ExecutorType:执行器的类型
* level:事务级别
* autoCommit:是否自动提交事务
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
2.2、事务创建:
// getTransactionFactoryFromEnvironment,根据配置的数据源驱动获取相对应的事务
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment){
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
// 比如配置mysql数据源,在初始化Configuration时,指定了jdbc事务工厂。
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
// ....省略N个配置项
}
// transactionFactory.newTransaction创建事务,存储连接、数据源、事务级别等信息
public enum TransactionIsolationLevel {
NONE(Connection.TRANSACTION_NONE), // 无事务
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED), // 不可重复读
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED), // 读未提交
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ), // 可重复读
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE); // 串行化
}
2.3、执行机创建
// 根据事务和执行机类型创建执行机(默认不指定则为SimpleExecutor)
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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
// 执行机加入到每个拦截器executor = (Executor) interceptorChain.pluginAll(executor);
// 说明这个执行器需要被各个拦截器拦截处理
private final List<Interceptor> interceptors = new ArrayList<>(); // 所有的拦截器
public Object pluginAll(Object target) {// target执行机
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
2.3.1、执行机区别
a、SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象
b、ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象
c、BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中addBatch(),等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同
作用范围:SqlSession生命周期范围内
d、CachingExecutor:默认开启,同个作用域下的缓存执行机,使用内存的,将数据保存到缓存中,这样可以有效的解决增删改查性能
2.3.2、拦截器
// 拦截器可以自定义,但是只能定义以下几种类,因为Configuration只加载这几类
// 默认的拦截器顺序:Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
// 第一种:源码对ParameterHandler类的处理,对参数的拦截
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
// 第二种:源码对ResultSetHandler类的处理,对结果集的拦截,可以对返回的结果进行处理
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
// 第三种:源码对StatementHandler类的处理,对查询前的拦截,可以对最终sql作修改
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;
}
自定义实现拦截器
@Intercepts({@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class})})
public class SqlStatusInterceptor implements Interceptor {// 实现接口方法}
2.4、创建DefaultSqlSession
// 默认为DefaultSqlSession,顶级接口SqlSession,作用是操作sql命令、获取mapper和管理事务
new DefaultSqlSession(configuration, executor, autoCommit);
3.1、根据jdk动态代理获取指定Mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 会根据mapperRegistry这个map获取对应的UserMapper.xml
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// 而mapperRegistry内部的关键代码
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// ....
mapperProxyFactory.newInstance(sqlSession);
// ....
}
// mapperProxyFactory.newInstance(sqlSession);
// 此处内部由MapperProxyFactory创建jdk的代理对象(Proxy对象),
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
// 而knownMappers对象则是在加载配置文件的时候动态addMapper进去的
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
至此,根据SqlSession,可以由getMapper获取指定的代理对象。
而代理对象怎么调用sql命令呢?原理则是根据MappedStatement对象。
4.1、根据代理对象查询
List<User> userList = userMapper.getUserByUsername("admin");
// 该代理对象先获取MappedStatement对象,获取UserMapper.xml信息
// 根据该xml信息去执行相应的sql语句
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
// ...
MappedStatement ms = configuration.getMappedStatement(statement);
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
// ...
}
4.2、MappedStatement对象
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
// 该对象的值是在初始化配置文件时加载的
// 过程:XMLConfigBuilder->XMLMapperBuilder->XMLStatementBuilder->MapperBuilderAssistant
// key:全包名,value:
private void parseConfiguration(XNode root) {
// ...
mapperElement(root.evalNode("mappers"));
// ...
}
// 解析UserMapper.xml的mapper元素
private void mapperElement(XNode parent) throws Exception {
// ...
for (XNode child : parent.getChildren()) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
// ...
}
// 解析每个节点
public void parseStatementNode() {
// ...
builderAssistant.addMappedStatement(...);
// ...
}
// 增加到MappedStatement对象中
public MappedStatement addMappedStatement(...) {
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(...);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
// 关键代码
configuration.addMappedStatement(statement);
return statement;
}
4.3、Excutor对象执行查询
获取了MappedStatement对象,则由执行机将该对象xml信息转换成BoundSql(最终SQL语句),继而执行sql命令
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
// query方法内部调用-->queryFromDatabase方法内部调用-->(SimpleExcutor)doQuery方法为jdbc实现
@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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
5、总结(核心代码总结)
1、获取配置文件mybatis-config.xml
2、由SqlSessionFactoryBuilder对象,读取配置文件
3、根据配置文件设置MapperRegistry对象(存放接口代理对象)
4、根据配置文件设置MappedStatement对象(存放接口对应的xml信息)
5、根据配置文件解析TransactionFactory事务对象(如mysql为JdbcTransactionFactory)
6、根据配置文件解析Excutor对象(默认SimpleExcutor执行机)
7、将Excutor加入拦截器(拦截器顺序Executor\ParameterHandler\StatementHandler\ResultSetHandler)
8、返回SqlSessionFactory对象,由SqlSessionFactory创建SqlSession 对象
9、调用SqlSession中的api,获取指定的接口代理对象(根据MapperRegistry对象获取)
3、调用SqlSession中的api,获取指定UserMapper.class文件
10、该代理对象调用查询方法,由拦截器拦截,获取指定的接口对应xml信息,组装BoundSql(SQL语句)对象
11、根据BoundSql对象,获取SQL语句,由驱动(如mysql为jdbc)调用sql命令,查询数据库。
参考资料:
https://blog.csdn.net/weixin_43184769/article/details/91126687
https://zhuanlan.zhihu.com/p/59844703