介绍
MyBatis是优秀的ORM框架,封装JDBC。可以通过XML或者注解的方式配置SQL。
执行原理
- 初始化。解析默认配置文件
mybatis-config.xml
。将XML中的配置保存到Configration
对象中。并以Configration
对象为参数生成SqlSessionFactory
。Configration
包括-
mapperRegistry:MapperRegistry
:该对象内部维护一个HashMap
,key是<mapper>
标签中的namespace接口Class,value是MapperProxyFactory
对象。MapperProxyFactory
作用是生成一个代理对象,下文会详细说明。 -
mappedStatements:Map<String, MappedStatement>
:是一个继承HashMap
的StrictMap
对象,key是namespace.方法ID
的字符串,value是MappedStatement
对象。MappedStatement
封装了<select>
标签级别的全部信息,包括返回值,入参,SQL。 -
caches:Map<String, Cache>
:是一个继承HashMap
的StrictMap
对象,key是namespace
,value是缓存,主要作用于二级缓存。
-
//XMLMapperBuilder中解析Mapper文件
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置建造器当前构造的namespace
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
//设置二级缓存,保存到configration中caches,key为namespace
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
//设置增删改查,保存到configration中的mappedStatements,key为namespace.方法id
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
- 查询。使用
SqlSessionFactory
生成一个SqlSession
对象。SqlSession
是对一次数据库执行动作的抽象,该对象中包含Executor
实现类对象和Configration
对象。- 使用接口Class构建代理对象,从
Configration
的MapperRegistry
中取出接口Class对应的MapperProxyFactory
对象。MapperProxyFactory
会先创建InvocationHandler
的实现类MapperProxy
对象,然后使用JDK动态代理创建接口的代理对象。 - 代理对象调用方法,被
MapperProxy
拦截执行其invoke
方法。先以方法的全限定名字符串去Configration
的mappedStatements
中取出对应的MappedStatement
对象。然后Executor
执行MappedStatement
,返回结果。
- 使用接口Class构建代理对象,从
//MapperRegistry中获取MapperProxyFactory
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//接口Class获取MapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//生成代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
//MapperProxyFactory生成代理对象
public T newInstance(SqlSession sqlSession) {
//初始化InvocationHandler实现类MapperProxy
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//接口Class.ID为key获取MappedStatement
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();
}
}
缓存
MyBatis分一级缓存和二级缓存,
- 一级缓存是
SqlSession
级别,又称本地缓存。 - 二级缓存是namespace级别,又称全局缓存。
- 准备,
SqlSessionFactory
创建SqlSession
。Configration
会创建一个Executor
对象放入SqlSession
中。可以根据ExecutorType
创建对应的Executor
对象。默认是创建SimpleExecutor
对象,先调用父类BaseExecutor
构造器,初始化PerpetualCache
,这时PerpetualCache
对象就是一级缓存。二级缓存开关cacheEnabled
开启情况下,会创建SimpleExecutor
的委托类CachingExecutor
。CachingExecutor
中TransactionalCacheManager
是二级缓存管理器。- 一级缓存,只有
PerpetualCache
对象, - 二级缓存,默认情况
PerpetualCache
经过LruCache
、SerializedCache
、LoggingCache
、SychronizedCache
层层委托。 -
TransactionalCacheManager
,内部维护名为transactionalCaches的HashMap
,key就是MapperStatement
中初始化的Cache,默认是SychronizedCache
,value是TransactionalCache
。在transactionalCaches第一次获取value时,将初始化的Cache委托给TransactionalCache
。 -
TransactionalCache
,在内部有名为entriesToAddOnCommit的HashMap
,SqlSession
执行一次查询SQL将结果临时保存到entriesToAddOnCommit。
- 一级缓存,只有
- 查询,
Executor
执行MappedStatement
。这时Executor
就是CachingExecutor
对象。保存过程,先调用SimpleExecutor
的query方法。查询结果保存到PerpetualCache
,实现一级缓存。在SqlSession
关闭时,执行commit动作,将entriesToAddOnCommit中的临时数据保存到TransactionalCache
中,实现二级缓存。
//Configration中创建executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//创建BaseExecutor实例
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);
}
//如果二级缓存开关开启,就委托给CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
//二级缓存
//CachingExecutor执行查询
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获取初始化后的Cache
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
//到TransactionalCacheManager中TransactionalCache中查询是否有缓存好的结果
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//先临时保存到TransactionalCache中entriesToAddOnCommit:HashMap
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//TransactionalCacheManager中获取缓存数据
public Object getObject(Cache cache, CacheKey key) {
//先以初始化Cache为key获取TransactionalCache
//然后到TransactionalCache获取数据
return getTransactionalCache(cache).getObject(key);
}
//TransactionalCacheManager中初始化TransactionalCache
private TransactionalCache getTransactionalCache(Cache cache) {
//如果Cache对应的TransactionalCache没有,就以Cache为参数初始化
return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
//TransactionalCache保存查询结果
private void flushPendingEntries() {
//entriesToAddOnCommit数据放到Cache
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
MyBatis二级缓存默认依赖性
resultmap
1.一对多映射
<resultMap>
<collection select="嵌套查询"></collection>
</resultMap>
2.多对一映射
<resultMap>
<association></association>
</resultMap>
引用
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps
网友评论