Mybatis一知半解
mybatis核心类及接口
Configuration:
mybatis核心配置类,用于管理mybatis相关的配置信息
当调用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,该方法继续调用mapperProxyFactory.newInstance方法来生成一个具体的代理对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public <T> T getMapper(Class<T> type, org.apache.ibatis.session.SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
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);
}
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}SqlSessionFactoryBuilder:
mybatis初始化时,调用XMLConfigBuilder读取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,构建mybatis运行的核心对象Configuration,然后将其作为参数构建一个SqlSessionFactory对象
其中XMLConfigBuilder在构建Configuration对象时,也会调用XMLMapperBuilder来读取*Mapper,XMLMapperBuilder会使用XMLStatementBuilder来读取和构建所有的SQL语句
1
2
3
4
5
6XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}SqlSession:
mybatis工作的主要接口,可以通过此接口执行SQL语句、获取mapper、管理事务等;SqlSession的执行,实际是通过对应Executor来进行的
1
2
3
4
5public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
......
}SqlSessionFactory:
创建SqlSession的工厂类接口
SqlNode:
sql节点接口,相关实现类:IfSqlNode、TrimSqlNode、ChooseSqlNode、TextSqlNode等
1
2
3public interface SqlNode {
boolean apply(DynamicContext context);
}Executor:
SqlSession的Sql执行,都是委托给Executor实现的,
1
2
3Executor
BaseExecutor CachingExecutor
SimpleExecutor ReuseExecutor BatchExecutor其中,BaseExecutor定义了几个抽象方法,该方法的具体实现交由子类完成
1
2
3
4
5BaseExecutor.java
protected abstract int doUpdate(xxx)
protected abstract List<BatchResult> doFlushStatements(xxx)
protected abstract <E> List<E> doQuery(xxx)SimpleExecutor:每次执行update或select时,会开启一个新的Statement对象,执行完立马关闭该对象
ReuseExecutor:每次执行update或select时,先从StatementMap(Map<String,Statement>)中根据sql查找对应的Statement对象,如果存在,直接取出使用,如果不存在,新建一个Statement对象,执行完后不关闭,而是将其放置到StatementMap中
BatchExecutor:执行update时,如果当前要执行的sql和Statement对象和保存的sql和Statement对象不相同,将当前要执行的Statement对象放到StatementList(List
)中,并保存此次执行的sql和Statement对象 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
......
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
org.apache.ibatis.executor.BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new org.apache.ibatis.executor.BatchResult(ms, sql, parameterObject));
}
......
// 这里没有直接通过statement对象执行SQL操作
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
// 真正执行SQL操作是在doFlushStatements方法中进行的,当Executor执行doQuery方法或commit方法(如SqlSession执行commit方法)时,会调用flushStatements方法
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
......
batchResult.setUpdateCounts(stmt.executeBatch());
......
}
BaseExecutor.java
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache();
flushStatements();
if (required) {
transaction.commit();
}
}
SqlSession、Executor、PerpetualCache层级关系
SqlSession <<<<<< Executor <<<<<< PerpetualCache
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class DefaultSqlSession implements SqlSession {
private final Executor executor;
......
}
public abstract class BaseExecutor implements Executor {
protected PerpetualCache localCache;
......
}
public class PerpetualCache implements Cache {
private final Map<Object, Object> cache = new HashMap<>();
......
}
mybatis缓存
mybatis中的缓存功能由根接口
Cache
定义,整个体系采用装饰器设计模式,数据存储和缓存的基本功能由PerpetualCache
永久缓存实现,然后根据一系列的装饰器对PerpetualCache
类进行缓存策略等方面的控制1
2
3
4
5
6
7public interface Cache {
/**
* @return The identifier of this cache
*/
String getId();
......
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15FifoCache:先进先出算法,缓存回收策略
LoggingCache:输出缓存命中的日志信息
LruCache:最近最少使用算法,缓存回收策略
ScheduledCache:调度缓存,负责定时清空缓存
SerializedCache:缓存序列化和反序列化存储
SoftCache:基于软引用实现的缓存管理策略
SynchronizedCache:同步的缓存装饰器,用于防止多线程并发访问
WeakCache:基于弱引用实现的缓存管理策略一级缓存,又叫“本地缓存”,是
PerpetualCache
类型的永久缓存,位于Executor
中,由1可知,Executor又位于SqlSession
中,因此,一级缓存的生命周期和SqlSession的生命周期是一致的二级缓存,又叫“自定义缓存”,实现了
Cache
接口的类都可以作为二级缓存;二级缓存根据namespace
命名空间作为唯一标识,保存在Configuration
核心配置对象中;二级缓存默认缓存类型为PerpetualCache,如果配置的缓存为默认类型,则mybatis会根据配置自动追加一系列装饰器1
2
3
4public class Configuration {
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
......
}Cache对象之间的引用顺序为:
SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache