Mybatis组件概念及其基本执行流程与源码解析
Mybatis组件核心概念
在开始学习源码之前要先学习Mybatis组件的概念。
- Configuration
Configuration是管理 mysql-config.xml 全局配置关系类,通俗理解就是通过mysql-config.xml封装成为Configuration供Mybatis配置使用。 - SqlSessionFactory
SqlSessionFactory是用来管理与数据库会话的接口。即通过SqlSessionFactory创建与数据库的会话(SqlSession)。 - Session
Session即SqlSession是面向程序员的接口。SqlSession中提供了对数据库的操作。 - Executor
Executor是一个接口,执行器有两大类基本执行器和缓存执行器。SqlSession内部通过执行器去操作数据库。 - MappedStatement
MappedStatement是底层封装对象,就是对操作数据库存储封装,包括 sql 语句、输入输出参数。如一个xxxx,就可以封装成一个MappedStatement。 - StatementHandler
StatementHandler具体实现对数据库的操作的接口。 - ResultSetHandler
ResultSetHandler是对数据库返回结果操作的接口。
Mybatis执行操作拆解
对源码的学习,首先要从整体执行流程拆解开始,以下面的Mybatis传统使用Demo为例:
//本例未使用mapper接口只是mapper.xml
public static void main(String[] args) throws IOException {
//1·获取mybatis配置文件输入流
InputStream configInputStream = Resources.getResourceAsStream("mybatis-config.xml");
//2·SqlSessionFactoryBuilder创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configInputStream );
//3·sqlSessionFactory创建与数据库的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4·SqlSession执行Mapper返回结果集
User user = sqlSession.selectOne("com.zzr.mybatis.UserMapper.selectUser", 1);
}
接下来进行分步详细拆解
1·获取mybatis配置文件输入流
基本的IO操作不做讲解。
2·SqlSessionFactoryBuilder创建SqlSessionFactory
以下代码只包含了关键代码。
public class SqlSessionFactoryBuilder {
//1·SqlSessionFactoryBuilder.build(inputStream);是读取配置文件的入口
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//2·XMLConfigBuilder 是Configuration的构建器。在其内部会将inputStream构建成XPathParser
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//3.parser.parse()解析配置文件并创建Configuration
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
//4.使用SqlSessionFactory子类DefaultSqlSessionFactory构建SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
parser.parse()解析
public class XMLConfigBuilder extends BaseBuilder {
protected final Configuration configuration;
//1·构建Configuration先判断配置文件是否已经加载到内存
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//2·从配置文件生成的XPathParser中获取节点configuration
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//3·通过解析XNode,组装Configuration(重点关注获取mappers节点的解析)
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//4·从/configuration节点封装的XNode中获取/properties节点的XNode
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
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);
}
}
}
mapperElement(root.evalNode(“mappers”))解析
public class XMLConfigBuilder extends BaseBuilder {
//可以看出获取mapper.xml的方式除了包扫描,还有通过资源文件,通过url和通过class的方式。
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
//在此将mapper保存到configuration的MapperRegistry缓存集合knownMappers中
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
//在此将mapper保存到configuration的MapperRegistry缓存集合knownMappers中
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
//在此将mapper保存到configuration的MapperRegistry缓存集合knownMappers中
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
}
mapperParser.parse();解析
关于mapperParser.parse();解析涉及到以下使用方式
使用Mapper接口方式操作数据库。
public class XMLMapperBuilder extends BaseBuilder {
//1·mapperParser.parse()
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//此处会遍历Mapper中的所有标签如select,insert等,将其封装成MappedStatement,
//放到Configuration的Map<String, MappedStatement> mappedStatements中。
//此处更加印证了Configuration为mybatis的统一配置类
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
//2·解析命名空间如com.zzr.mybatis.xxx
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
//3·将其加入到mapperRegistry的集合中
configuration.addMapper(boundType);
}
}
}
}
}
//4·主要用来缓存Mapper接口的代理工厂类
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//5·用namespace的接口创建MapperProxyFactory
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
//6·Mapper.java的代理工厂类
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 mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//7·创建mapperProxy ,mapper代理类,执行具体逻辑使用。Mapper中的每一个method都可以创建mapperProxy 。
//此处代码量比较多就不展示代码了。就是利用mapper的接口地址和方法名找到mapper.xml中的标签,
//并根据标签类型(select,update,insert等)创建MapperProxy。
//使用UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.selectUser(1)这种方式执行时才会使用MapperProxy。
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
SqlSessionFactory至此创建完成。
3·sqlSessionFactory创建与数据库的会话
上面已经讲解过,实际使用的sqlSessionFactory为其子类DefaultSqlSessionFactory。所以从DefaultSqlSessionFactory入手。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
//1·获取会话连接,从中可以看出无参openSession,获取session默认使用SimpleExecutor,不开启自动提交
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//2·创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//3·通过事务和执行器类别获取执行器
final Executor executor = configuration.newExecutor(tx, execType);
//4·使用SqlSession子类DefaultSqlSession创建Session
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();
}
}
}
configuration.newExecutor(tx, execType)解析
因为在创建sqlSessionFactory之前就已经创建好了configuration,所以通过configuration中的一些配置创建Executor
public class Configuration {
//默认开启一级缓存
protected boolean cacheEnabled = true;
//创建执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//1·判断执行器类型,因为我们使用的是默认的执行器,所以是SimpleExecutor
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);
}
//2·一级缓存开启,创建缓存执行器
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//3·将缓存执行器加入到mybatis拦截器链中。
//缓存执行器虽然与基本执行器都实现了Executor接口,但是缓存执行器是作为mybatis插件存在的也就是拦截器,
//在执行真正的sql之前会先去执行缓存执行器中的代码
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
//Mybatis拦截器链相关代码,责任链模式拦截器
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
//增加拦截器(插件),Mybatis的分页插件也是使用这个类实现添加
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
以上就是获取SqlSession的代码讲解。
4·SqlSession执行Mapper返回结果集
从以上讲解得知,实际使用的SqlSession为DefaultSqlSession。
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T selectOne(String statement) {
//1·调用selectOne
return this.<T>selectOne(statement, null);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
//2·调用selectselectList。
//因为这里我们的Demo使用的selectOne,所以如果查出多条记录会报出异常
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
//3·调用selectList
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
//4·真正执行逻辑的selectList
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//5·获得MappedStatement,即sql标签的封装(statement=命名空间+id(方法名))
MappedStatement ms = configuration.getMappedStatement(statement);
//6·此处代码过于繁琐。简单讲解一下。
//首先会先执行缓存执行器去查询是否存在缓存,不存在就会去执行基本执行器
//基本执行器会创建StatementHandler并使用其query查询执行结果。
//此时就会看见与传统jdbc类似的代码。之后ResultSetHandler操作结果集返回。
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();
}
}
}
以上就是执行Mapper返回结果集。
附上基本的执行流程:
SqlSessionFactoryBuilder创建SqlSessionFactory
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
>org.apache.ibatis.builder.xml.XMLConfigBuilder 构造函数
>org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
>org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration mybatis-config.xml内容
>org.apache.ibatis.parsing.XPathParser.evaluate
>org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement
>org.apache.ibatis.session.SqlSessionFactoryBuilder.build(org.apache.ibatis.session.Configuration)
>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.DefaultSqlSessionFactory
拿到SqlSession 进行对我们的执行器进行初始化 SimpleExecutor
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession()
>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource
>org.apache.ibatis.transaction.TransactionFactory.newTransaction(javax.sql.DataSource, org.apache.ibatis.session.TransactionIsolationLevel, boolean)
>org.apache.ibatis.session.Configuration.newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)
>org.apache.ibatis.executor.SimpleExecutor
>org.apache.ibatis.executor.CachingExecutor 一级缓存 自动
>org.apache.ibatis.plugin.InterceptorChain.pluginAll 责任链模式拦截器
sqlSessionFactory创建与数据库的会话,SqlSession执行Mapper返回结果集
org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(java.lang.String, java.lang.Object)
>org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(java.lang.String, java.lang.Object)
>org.apache.ibatis.session.Configuration.getMappedStatement(java.lang.String)
>org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
>org.apache.ibatis.executor.CachingExecutor.createCacheKey 缓存的key
>org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
>org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
>org.apache.ibatis.executor.BaseExecutor.doQuery
>org.apache.ibatis.executor.statement.PreparedStatementHandler.query
>org.apache.ibatis.executor.resultset.ResultSetHandler.handleResultSets
>org.apache.ibatis.executor.resultset.DefaultResultSetHandler