使用Mybatis执行数据库操作,首先要获取SqlSession,通过它进一步获取Mapper接口代理对象,最后通过代理对象发起数据库操作
这是使用Mybatis进行数据库操作的一个Demo,通过构建DataSource、TransactionFactory、Environment、Configuration并将它们组装在一起获得SqlSessionFactory,此后就可以通过它获取SqlSession
sqlSessionFactory.openSession();通过会话工厂开启一个会话@Test
public void readerTest() throws IOException {
PageUtil.setPagingParam(1,11);
//得到一个Reader处理类(字符流)
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/mytest/mybatis-config.xml")) {
// environment:数据源ID 在mybatis-config.xml 标签里面配置的 id 为development的数据源信息(非必传)
// properties :配置文件信息。一般是数据库配置信息。可以在这里进行配置 也可以在mybatis-config.xml 通过 标签进行引入properties配置文件
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,"development", Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
}
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.insertStudent(new StudentDto(11, "12", 24, Arrays.asList(1, 2, 3, 4, 5)));
sqlSession.commit();
List rs = mapper.getStudentsByAlias();
System.out.println(rs.toString());
}
复制代码
看下openSession里面是怎么实现的
1.得到配置的环境数据源信息
2.根据数据源信息得到一个事务工厂
3.新建一个事务
4.根据事务得到执行器(代表一个事务对应一个执行器)
5.创建默认的SqlSession对象
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);
// 拿到执行器Executor
final Executor executor = configuration.newExecutor(tx, execType);
// 创建默认的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();
}
}
复制代码
看下执行器的创建过程
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);
}
// cacheEnabled一级缓存默认是开启的,为true的话代表开启二级缓存
// 二级缓存通过装饰器模式的方式加载进执行器中
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 重点:插件拦截器过滤链 把执行器怼到里面去
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
复制代码
看下插件的植入过程
mybatis会遍历所有配置的插件来进行代理。多个插件会重复代理
public static Object wrap(Object target, Interceptor interceptor) {
// 获取自定义插件中,通过@Intercepts注解指定的方法
Map, Set> signatureMap = getSignatureMap(interceptor);
// 获取目标对象的类型
Class> type = target.getClass();
// 根据类型获取所有拦截的接口
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 如果需要拦截,则用JDK动态代理生成一个代理对象
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
复制代码
看下Plugin
Plugin有三个参数
target:目标对象Executor、ParameterHandler、ResultSetHandler、StatementHandler实例 或者另一个插件代理对象。
interceptor:对应插件类加载对象
signatureMap:插件注解生效的方法
package org.apache.ibatis.plugin;
import org.apache.ibatis.reflection.ExceptionUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map, Set> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map, Set> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
public static Object wrap(Object target, Interceptor interceptor) {
// 获取自定义插件中,通过@Intercepts注解指定的方法
Map, Set> signatureMap = getSignatureMap(interceptor);
// 获取目标对象的类型
Class> type = target.getClass();
// 根据类型获取所有拦截的接口
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 如果需要拦截,则用JDK动态代理生成一个代理对象
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
// 如果是@Intercepts注解指定的方法,就调用拦截的逻辑
if (methods != null && methods.contains(method)) {
// 将目标方法信息封装成Invocation给拦截器的intercept()方法使用
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
private static Map, Set> getSignatureMap(Interceptor interceptor) {
// 获取@Intercepts注解信息
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
// 获取@Signature注解信息
Signature[] sigs = interceptsAnnotation.value();
Map, Set> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
// 把所有@Signature指定拦截的组件、方法添加到map中去
Set methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
private static Class>[] getAllInterfaces(Class> type, Map, Set> signatureMap) {
Set> interfaces = new HashSet<>();
while (type != null) {
for (Class> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class>[0]);
}
}
复制代码
可以看出,创建sqlsession经过了以下几个主要步骤:
- 从配置中获取Environment;
- 从Environment中取得DataSource;
- 从Environment中取得TransactionFactory;
- 从DataSource里获取数据库连接对象Connection;
- 在取得的数据库连接上创建事务对象Transaction;
- 创建Executor对象(该对象非常重要,事实上sqlsession的所有操作都是通过它完成的);
- 创建sqlsession对象。



