MyBatis源码相对于Spring应该是层次比较清晰,容易理解的;其实简单来说就是解耦、封装,让开发者更关注业务层的开发,实现简单而又方便的调用。
本文主要介绍mybatis框架的几个重要点:xml文件加载初始化、mapper接口动态代理加强和源码的一些模块以及所涉及的设计模式;通过阅读本文之后你将会学到Mybatis的工作流程以及常用的框架技术和模式。
文章目录
注:以下介绍主要是基于使用注解的SpringBoot配置。
一 、xml文件加载初始化
Mybatis加载XML文件核心类:
XMLConfigBuilder:解析Mybatis-config.xml文件节点</configuration>;这一块可以忽略,因为现在几乎都是Spring集成Mybatis,通过application.properties来配置,跳过config文件解析直接进入第二阶段XMLMapperBuilder解析mapper.xml文件。
XMLMapperBuilder:解析mapper.xml文件节点</mapper>;Mybatis结合springBoot或者Spring使用时,在配置dataSource创建SqlSessionFactory时启动的XMLMapperBuilder对象。主要有ResultMap、Sql节点等。
XMLStatementBuilder:解析select|insert|update|delete,封装成MappedStatement。
bing绑定:绑定mapper class和对代理工厂对象,mapperRegistry是其注册中心;创建Mapper接口的代理工厂类MapperProxyFactory,该类主要为Mapper接口方法生成代理类(MapperProxy(JDK动态代理))进行增强。
集成SpringBoot初始化XML:
配置数据源时SqlSessionFactory是必不可少,它是生成SqlSession的工厂类(默认DefaultSqlSessionFactory);而SqlSessionFactory是由SqlSessionFactoryBean实例化的如下图代码:
- MapperLocations 就是设置Mapper文件资源的;
- plugins是引入插件例如分页插件,关于插件第三节中详细介绍;
- 加载解析xml,在getObject()------->afterPropertiesSet()------->buildSqlSessionFactory(),最后这个方法会真正调用Mybatis加载XML文件核心类加载、初始化mapper文件,这边主要介绍Mapper是如何绑定代理工厂的。
注意BeanFactory和FactoryBean的区别
public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(datasource); // 设置mybatis的xml所在位置 bean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath*:mapper/test1/*.xml")); bean.setPlugins(new Interceptor[]{DefinePageInterceptor.buildPageInterceptor()}); // FactoryBean就是通过getObject()获取真正的bean实例(注意BeanFactory和FactoryBean的区别) return bean.getObject(); }
Mapper是 如何绑定代理工厂(注意文件中的注释):
// 1、XMLMapperBuilder 为Mapper绑定代理工厂
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
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);
// 1. 绑定Mapper的代理工厂MapperProxyTactory---入口
configuration.addMapper(boundType);
}
}
}
}
-----------------------------------------------------------------------------
// 1、MapperRegistry 注册Mapper对应的代理工厂类
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 {
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);
}
}
}
}
二、Mapper接口动态代理加强
问题思考:Mybatis是如何通过Mapper接口进行sql操作的?
MapperProxy就是Mapper接口的代理类,这个代理类由代理工厂类MapperProxyFactory生成如图所示(此工厂类在第一步中已经将Mapper接口类型作为key进行缓存),MapperProxy的必须属性参数有mapper相关的sqlsession,mapper接口类型Class。
// 1、MapperRegistry:注册中心获取代理工厂
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 {
// 2、代理工厂创建代理类
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
-------------------------------------------------------------------------------------
// 1、MapperProxyFactory 代理工厂类创建代理
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<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
1、动态代理:当调用Mapper接口中的方法就会交由MapperProxy进行代理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 1、获取MapperMethod 执行execute(method的执行是放在MapperMethod中的)
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 2、MapperMethod中根据SqlCommand的类调用sqlSession的 insert|update.....
return mapperMethod.execute(sqlSession, args);
}
// 3、先取缓存如果没有,创建一个新的(封装了mapper接口、要执行的方法以及贯穿整个mybatis的Configuration)
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
method.execute(sqlsession,args)真正 调用sqlSession的地方。走进去看一下sqlSession触发不同的sql类型,但是对于select由于返回类型可能不同,再继续处理。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据sqlCommand类型执行insert|update
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:
// 2、根据select返回类型的不同执行不同的方法,,其最终都会执行selectList,只是前期准备不同。
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);
}
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;
}
2、Executor执行:其实最终还是要使用Executor执行的
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 1、根据namespace获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 2、wrapCollection()封装参数值
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();
}
}
Executor有两个实现类,类图如下;Executor是由Configuration创建的,并且使用了装饰器模式以及动态代理进行加强(interceptorChain.pluginAll(executor))
// 1、DefaultSqlSessionFactory 中创建DefaultSqlSession
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);
// 2、DefaultSqlSessionFactory中创建DefaultSqlSession
final Executor executor = configuration.newExecutor(tx, execType);
// 3、封装参数到SqlSession中
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();
}
}
------------------------------------------------------------------------------------
// 1、Configuration中创建Executor
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
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);
}
// 2、装饰器模式对基础的Executor进行装饰
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 3、遍历所有的拦截器,然后调用各个拦截器的plugin方法,然后使用Plugin.wrap()为executor生成
Plugin代理对象(封装了interceptor、待拦截的method、target对象)。
// 拦截器链InterceptorChain会对每一个拦截器依次封装在代理对象中(即:realObject -->
proxy1RealObject --> prox2Prox1RealObject)只要符合拦截条件都会被依次代理增强。拦截执行
的时候会倒序依次执行(prox2Prox1RealObject-->proxy1RealObject -->realObject)
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
ReuseExecutor:对Statement进行了缓存,重复利用。
以SimpleExecutor为例看其处理过程:
下图中doQuery()方法第四行代码configuration.newStatementHandler():在Configuration类中通过RoutingStatementHandler(静态代理)根据MappedStatement的StatementType(默认PREPARED)生成哪一种StatementHandler(类图如下),
@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());
// 1、sql执行---最终会使用PreparedStatementHandler (如下代码)
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 是否对获取的连接进行log加强
Connection connection = getConnection(statementLog);
// 获取Statement ----最终会调用instantiateStatement()如下代码
stmt = handler.prepare(connection, transaction.getTimeout());
// 参数设值 --- 如下代码
handler.parameterize(stmt);
return stmt;
}
-----------------------------------------------------------------------------------------
// 1、PreparedStatementHandler 使用Statement执行execute() (和使用jdbc一模一样的)
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 2、结果集处理见(ResultSetHandler处理小节)
return resultSetHandler.<E> handleResultSets(ps);
}
// 3、PreparedStatementHandler 使用connection获取Statement (和使用jdbc一模一样的)
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
// 4、参数设值(获取ParameterMapping使用MetaObject(反射模块详细介绍)取值,然后占位符位置设值)
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
StatementHandler继承关系:
3、ResultSetHandler解析查询结果:结果解析(ResultSetHandler)唯一默认实现类DefaultResultSetHandler,也会被加强;
// 1、 Configuration中ResultSetHandler生成 (DefaultResultSetHandler默认唯一实现)
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;
}
----------------------------------------------------------------------------------------
// 2、DefaultResultSetHandler处理查询结果接上一小节
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 返回结果集处理封装对象。-----不详细介绍
while (rsw != null && resultMapCount > resultSetCount) {
// 3、获得resultMap,实体类和表中数据字段的对应关系
ResultMap resultMap = resultMaps.get(resultSetCount);
// 4、将值设置成对应的resultmap对象
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
........
return collapseSingleResultList(multipleResults);
}
总结:ResultSetHandler、StatementHandler职责分工明确、层次分明,jdbc操作数据库的整套代码被完全封装起来,其本质和逻辑还是一样的。
三、Mybatis涉及的模块以及设计模式使用
- 反射模块:查询的结果ResultSet通过反射构建bean。(实例化目标对象和对象属性赋值需要反射生成)---工厂模式
反射的核心类:
1)Reflector:存放class定义的属性、方法信息
2)DefaultReflectorFactory:依据class类新创建Reflector并缓存
3)MetaClass:引用ReflectorFactory、Reflector,可以获取类的元信息
4)ObjectWrapper:对bean、MetaClass的封装,可以实现对bean属性的设值以及获取
5)DefaultObjectFactory:实例化对象
6)MetaObject:主要封装了bean、ObjectWrapper、DefaultObjectFactory、DefaultReflectorFactory,因此可以通过MetaObject设置和获取bean的属性。(主要是通过ObjectWrapper操作)
- 用于实例化目标对象的类
------ObjectFactory:MyBatis每次创建结果对象的新实例时,使用ObjectFactory构建POJO,默认实现DefaultObjectFactory(反射实例化对象)
反射创建对象(注意区别):
- 用于对象属性赋值的类
------ReflectorFactory:创建Reflector的工厂类,Reflector是MyBatis反射模块的基础,每个Reflector对象都对应一个类,在其中缓存了反射操作所需要的类元信息。默认实现类DefaultReflectorFactory(读取类元信息封装在Reflector中,每一个类都有一个Reflector)。
Reflector(核心元信息缓存类):
------MetaObject:
MetaObject封装了对象元信息,包装了MyBatis中的五个核心的反射类,也是提供给外部使用的反射工具类,可以利用它读取或者修改对象的属性信息(其实也是通过ObjectWrapper调用相关的方法,例如BeanWrapper)。
// MetaObject 包装五个核心类
private final Object originalObject;
private final ObjectWrapper objectWrapper;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory reflectorFactory;
BeanWrapper作用如下:(是对bean的封装可以给bean属性设置值,也可以获取某个属性的值)
public class BeanWrapper extends BaseWrapper {
//1、BeanWrapper 封装了bean对象以及对应的类元信息,通过BeanWrapper get()和set()获取属性值或者设置属性值
private final Object object;
private final MetaClass metaClass;
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
this.object = object;
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
}
MetaClass(封装类元信息,并且也是通过Reflector来获取类的相关属性信息):
public class MetaClass {
// 1、封装 类的属性和方法信息,并且可以通过此类获取属性等信息(如方法返回类型,参数类型)
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
}
总结:反射模块核心类使用如下
public void mybatisReflect(){
DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory();
ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 1、DefaultObjectFactory创建bean
User user = defaultObjectFactory.create(User.class);
DefaultReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory();
// 2、创建MetaObject对象并在BeanWrapper中使用defaultReflectorFactory创建Reflector(Reflector加载了类元信息)
MetaObject metaObject = MetaObject.forObject(user, defaultObjectFactory, objectWrapperFactory, defaultReflectorFactory);
// 3、BeanWrapper
ObjectWrapper objectWrapper = metaObject.getObjectWrapper();
// 4、使用ObjectWrapper读取对象信息,并对对象属性进行赋值操作(最终调用Reflector获取method,然后通过反射设值)
objectWrapper.set(new PropertyTokenizer("userName"),"xiaodai");
String userName = user.getUserName();
System.out.println(userName);
}
2. 插件模块:
MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
-------执行器的拦截-----------------------------------
- ParameterHandler (getParameterObject, setParameters)
-------参数处理的拦截--------------------------------
- ResultSetHandler (handleResultSets, handleOutputParameters)
--------结果集处理的拦截----------------------------
- StatementHandler (prepare, parameterize, batch, update, query)
--------sql语法构建的拦截--------------------------
// 通过pluginAll实现对四大接口拦截设置
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
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;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler 其实是个静态代理,根据不同要求创建不同的对象
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
这4个方法实例化了对应的对象之后,都会调用interceptorChain的pluginAll方法,InterceptorChain的pluginAll就是遍历所有的拦截器,然后调用各个拦截器的plugin方法。注意:拦截器的plugin方法的返回值会直接被赋值给原先的对象。
在创建SqlSession的时候最先会执行的Configuration的newExecutor()并进行拦截器配置和加强。例:加入PageInterceptor拦截器,newExecutor()时会调用interceptorChain的pluginAll方法,然后在执行PageInterceptor.plugin(),最后会使用Plugin.wrap()为executor生成Plugin代理对象。最后在执行executor.query()时会被Plugin加强,根据PageInterceptor注解@Signature判断是否是要拦截的方法,PageInterceptor拦截主要是分页处理。
Plugin这个类的作用就是根据 @Interceptors注解,得到这个注解的属性 @Signature数组,然后根据每个 @Signature注解的type,method,args属性使用反射找到对应的Method。最终根据调用的target对象实现的接口(会和@Signature的type进行比较)决定是否返回一个代理对象替代原先的target对象
注:拦截的入口其实就是在代理类Plugin的invoke是真实对象之前根据判断@Signature解析的Method和classType是否和代理的对象的类型和方法一致来决定是否执行拦截器。
拦截器链InterceptorChain会对每一个拦截器依次封装在代理对象中(即:realObject --> proxy1RealObject --> prox2Prox1RealObject)只要符合拦截条件都会被依次代理增强。拦截执行的时候会倒序依次执行(prox2Prox1RealObject-->proxy1RealObject -->realObject)。
3. Logging模块可以引入第三方例如Slfj----------适配器模式:
mybatis只有属于自己一套的log接口,并没有所有本身的日志,那么如何和市面上面 流行的日志框架做整合呢?这个时候就需要用到适配器模式。
如上图实现mybatis的日志实现类都需要实现这个接口。这使用到LogFactory
==org.apache.ibatis.logging.LogFactory== 日志工厂
如上图 tryImplementation()先判断logConstructor是否为空,只有logConstructor为空的时候tryImplementation里面的方法才会执行。
例如:useSlf4jLogging() ---> 会获取Slf4jImpl的构造器,当使用LogFactory.getLogger()就会实例化,此时logConstructor已经有值,那么其它的try都不会在创建构造器。
4. 缓存模块--------Cache接口(装饰器模式)
实现类-->PerpetualCache:Cache的基本实现类
BlockingCache:它是个阻塞版本的缓存装饰器,保证只有一个线程到数据库中查找 key 对应的数据,使用 ConcurrentHashMap<Object, ReentrantLock> 实现了阻塞功能。
FifoCache:FifoCache 底层维护了一个队列,当需要清除时会清除队列头部的缓存
LruCache:LruCache底层维护了一个 LinkedHashMap 它是一种有序的 HashMap。当我们获取一个对象的时候会把 这个对象移动到 LinkedHashMap 的尾部表示最近访问的,需要清除的时候清除头部即可。
SoftCache&WeakCache:是 JVM 中定义的引用类型,是为了更方便的进行JVM垃圾回收的,针对那些只用一次的对象 进 行更高效率的回收。
MyBatis中涉及到动态SQL需要通过CacheKey来封装缓存的key值。
构成CacheKey对象的要素(如下图代码):
- mappedStatement的id
- 指定查询结果集的范围(分页信息)
- 查询所使用的SQL语句
- 用户传递给SQL语句的实际参数值
BaseExecutor的query:
cacheKey比较是否相等判断逻辑:
总结:MyBatis 的缓存接口 Cache 的基础实现 PerpetualCache 维护了一份 HashMap 作为缓存的实现。同时它还有很多装饰类,比如阻塞式缓存BlockingCache 内部维护了带对象同步器的 ConcurrentHashMap、带清除策略的 FifoCache/LruCache,FifoCache 内部维护了一个队列Dqueue,LruCache内部维护了一份 LinkedHashMap 。这些都使得缓存模块的功能很多,业务方可以根据自己的需求进行灵活组装。
一级缓存和二级缓存:
MyBatis 中的缓存就是说 MyBatis 在执行一次SQL查询或者SQL更新之后,这条SQL语句并不会消失,而是被MyBatis 缓存起来,当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,而不是再次执行SQL命令。
一级缓存:
mybatis的一级缓存是基于sqlsession的缓存,默认开启,不同sqlsession之间的缓存数据区域是不会相互影响的,如果会话失效,则缓存失效。
清除缓存:
当commit或者rollback的时候会清除缓存,并且当执行insert、update、delete的时候也会清除缓存。
mybatis一级缓存失效的四种情况
- sqlsession变了 缓存失效
- sqlsession不变,查询条件不同,一级缓存失效
- sqlsession不变,中间发生了增删改操作,一级缓存失败
- sqlsession不变,手动清除缓存,一级缓存失败
二级缓存:
二级缓存是手动开启的,作用域为mapper级别(也可以说MapperStatement级缓存,也就是一个namespace就会有一个缓存),不同的sqlsession两次执行相同的namespace下的sql,且向sqlq中传递的参数也相同,即最终执行相同的sql,则第一次中会从数据库查并存缓存,第二次会从内存查;因为二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的,也就是要求实现Serializable接口,如果存储在内存中的话,实测不序列化也可以的。
如果开启了二级缓存的话,你的Executor将会被装饰成CachingExecutor,缓存是通过CachingExecutor来操作的,查询出来的结果会存在statement中的cache中,若有更新,删除类的操作默认就会清空该MapperStatement的cache(也可以通过修改xml中的属性,让它不执行),不会影响其他的MapperStatement。
5、建造者模式:在解析mapper.xml时会通过MapperBuilderAssistant助理进行每一阶段的属性值的builder(例如addResultMap、addMappedStatement)。