mybaits sqlSession 源码解读

SqlSession

org.apache.ibatis.session.SqlSession 是 mybatis 操作sql、 获取mapper、处理事务的基本单元。一般意义上我们运用mybatis 都是在操作 sqlSession

类图如下:

mybaits sqlSession 源码解读

  • org.apache.ibatis.session.defaults.DefaultSqlSession 默认实现。
  • DefaultSqlSession 的初始化过程 SqlSessionFactoryBuilder --> DefaultSqlSessionFactory--> SqlSession

SqlSession模式一

  • 所有 <T> T selectOne、<E> List<E> selectList 均由<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)实现。源码:
[@Override](https://my.oschina.net/u/1162528)
 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
 try {
 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();
 }
 }
  • 源码分析: 该方法接受 3个参数
  • - statement sql语句的标识符,就是常用的 namespace.sqlId组合,具有唯一性
  • 通过此id 可以从mybatis配置类org.apache.ibatis.session.Configuration中获取 映射语句对象MappedStatement, id 跟语句的映射关系储存在 Configuration 下的内部数据结构(hashmap 字类) Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection") 中 - parameter 顾名思义 要传递给statement标识的sql语句的参数对象 - org.apache.ibatis.session.RowBounds 限制对象检索的界限。比方说 上面2个参数联合查出来200条,rowBounds的offset 和limit 形成一个区间 [20,150],截取在此区间的数据集。就是mybatis 逻辑分页 由 org.apache.ibatis.executor.Executor 操作返回查询结果
[@Override](https://my.oschina.net/u/1162528)
 public int update(String statement, Object parameter) {
 try {
 dirty = true;
 MappedStatement ms = configuration.getMappedStatement(statement);
 return executor.update(ms, wrapCollection(parameter));
 } catch (Exception e) {
 throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
 } finally {
 ErrorContext.instance().reset();
 }
 }

SqlSession模式二

sqlSession 操作mapper 接口分析

  • 获取Mapper

public <T> T getMapper(Class<T> type)的底层方法为 Configuration.<T>getMapper(type, this),也就是说获取Mapper接口是由配置类来完成的,具体是由其持有的 MapperRegistry 来维护。

MapperRegistry中的成员Map<Class<?>, MapperProxyFactory<?>> knownMappers 是Mapper 接口的注册表,所有注册的Mapper 将与之对应的MapperProxyFactory存储在该注册表中。底层是通过JDK 代理来进行代理

我们的业务接口通过MapperProxy 代理,代理对象通过MapperMethod执行方法

[@Override](https://my.oschina.net/u/1162528)
 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);
 }
 // 如果是抽象 没有实现构造 方法的代理
 final MapperMethod mapperMethod = cachedMapperMethod(method);
 return mapperMethod.execute(sqlSession, args);
 }

流程如下:

mybaits sqlSession 源码解读

  • 注册Mapper
  • 在分析了如何获取Mapper之后,再搞一搞如何注册Mapper,通过上面知道Mapper的获取是Configuration委托 MapperRegistry获取,同样的流程,提供了三种方式来注册
// 将指定包下面的指定接口进行加载
 public void addMappers(String packageName, Class<?> superType) {
 mapperRegistry.addMappers(packageName, superType);
 }
 //加载指定包下面的所有接口
 public void addMappers(String packageName) {
 mapperRegistry.addMappers(packageName);
 }
 //加载指定的接口
 public <T> void addMapper(Class<T> type) {
 mapperRegistry.addMapper(type);
 }
  • 核心基于MapperRegistry 下的方法:
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来对xml或者类进行解析 
 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
 parser.parse();
 loadCompleted = true;
 } finally {
 if (!loadCompleted) {
 knownMappers.remove(type);
 }
 }
 }
 }
  • 批量注册某个包下基于某个超类的 是对上面进行循环遍历进行
/**
 * @since 3.2.2
 */
 public void addMappers(String packageName, Class<?> superType) {
 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
 // 判断包下是否有该超类的实现 没有会抛出异常 主要起到检测加载作用
 resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
 // 获取清单并进行注册
 Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
 for (Class<?> mapperClass : mapperSet) {
 addMapper(mapperClass);
 }
 }
  • 注册包下所有的接口
/**
 * @since 3.2.2
 */
 public void addMappers(String packageName) {
 addMappers(packageName, Object.class);
 }
  • 由上面发现 重点是 MapperAnnotationBuilder,该类也十分重要的转换类,后面会继续分析

作者:NotFound403

原文:https://my.oschina.net/u/2948566/blog/2995801

著作权归作者所有