MyBatis 支持的扩展点(version:3.2.7)

时间:2023-03-10 00:45:14
MyBatis 支持的扩展点(version:3.2.7)

从 [MyBatis 原码解析(version:3.2.7)] 中,我们得知,MyBatis去执行SQL都是通过 DefaultSqlSession 中的工具方法去执行的。

那么问题来了,MyBatis 是怎么构造 DefaultSqlSession 的?

通过查看源码,得知 MyBatis 是通过 DefaultSqlSessionFactory 来构造 DefaultSqlSession 的。

DefaultSqlSessionFactory#openSessionFromDataSource(ExecutorType, TransactionIsolationLevel, boolean)

/**
* @param ExecutorType 执行器的类型。MyBatis中提供了三种执行器:SIMPLE, REUSE, BATCH。默认的是 SIMPLE
* @param TransactionIsolationLevel 事务隔离级别
* @param autoCommit 是否自动提交事务
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 通过 Environment 获取事务工厂 TransactionFactory。没有指定Environment,则使用 ManagedTransactionFactory
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 从 TransactionFactory 中获取一个 Transaction
final Executor executor = configuration.newExecutor(tx, execType); // 从 Configuration 中获取一个新的 Executor。(Configuration 对应的是 mybatis-config.xml 中的配置)
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(Transaction transaction, ExecutorType executorType)

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType; // 默认使用 SimpleExecutor
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction); // 使用 BatchExecutor
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction); // 使用 ReuseExecutor
} else {
executor = new SimpleExecutor(this, transaction); // 默认使用 SimpleExecutor
}
if (cacheEnabled) {
executor = new CachingExecutor(executor); // 使用 CachingExecutor
}
executor = (Executor) interceptorChain.pluginAll(executor); // 执行所有的MyBatis拦截器,并返回 Executor
return executor;
}

至此,我们找到了MyBatis的一个扩展点——拦截器interceptor。

MyBatis Inteceptor是使用JDK的动态代理来实现的,所以它只能对接口进行拦截。

里面两个很重要的注解是:@Intercepts、@Signature
@Intercepts : 标记要拦截的方法签名
@Signature : 方法签名,唯一的标记一个接口的方法

通过查看源码,我们还可以知道,MyBatis所有的代理拦截都是通过 InterceptorChain.pluginAll(Object target) 来实现的。
至此,我们得到下图:

MyBatis 支持的扩展点(version:3.2.7)

通过上图可知,Mybatis支持对 Executor 、 StatementHandler 、 ResultSetHandler 和 PameterHandler 进行拦截,也就是说会对这4种对象进行代理。

Executor                : 作用是执行SQL语句(所有的sql),并且对事务、缓存等提供统一接口。(在这一层上做拦截的权限会更大)
StatementHandler : 作用是对 statement 进行预处理,并且提供统一的原子的增、删、改、查接口。(如果要在SQL执行前进行拦截的话,拦截这里就可以了)
ResultSetHandler  : 作用是对返回结果ResultSet进行处理。
PameterHandler    : 作用是对参数进行赋值。

附:

对 TypeHandler 进行扩展:http://mp.weixin.qq.com/s/GLb_-dLVAWYb6-_GWGIF8w
解决问题:
  从SqlServer中取数据,遇到很多列都是Numeric(10,2)类型,指的是字段是数字型,长度为10,小数为两位。Mybatis默认的BigDecimalTypeHandler取到后,都默认变成4位小数,不够的补了0。而上层的要求是,拿到的和数字相关的数据都要2位小数。这时,可以自定义一个 TypeHandler 进行统一处理

TypeHandler 的作用:无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器 TypeHandler 将获取的值以合适的方式转换成 Java 类型。