Mybatis运行过程理解

Mybatis的前身是Apache的开源项目iBatis, 2013年后迁移到GitHub并提供维护,是一个基于Java的持久层框架。
其优势在于:Mybatis几乎可以替代JDBC,同时提供了接口编程,数据访问层DAO不需任何实现类,只需一个接口和XML(或注解)。
一个优秀的框架必定包含了设计者精妙的设计思想,通过源码的阅读可以更好的理解运行原理。

Demo

这是摘自Mybatis官方文档的一段代码,是单独使用Mybatis的基本用法。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
// Mapper接口实现
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
} finally {
  session.close();
}

注:Mybatis实现方式分为SqlSession API实现和Mapper接口实现,Demo代码为接口实现

运行过程解析

很多优秀的博客已经对Mybatis源码进行了分析,在这里我主要进行运行过程的梳理和关键类的分析。

Mybatis的运行过程可以分为 两大步:
1. 读取配置文件缓存到Configuration对象,用以创建SqlSessionFactory对象
2. SqlSession的执行过程
- 这一步需要先掌握反射技术和动态代理技术

1. 构建SqlSessionFactory过程

涉及的设计模式:
Builder模式 -> 通过SqlSessionFactoryBuilder去构建SqlSessionFactory
Singleton模式 -> 几乎所有的配置内容都会放在这个单例对象Configuration中

关键步骤:
1.通过org.apache.ibatis.builder.xml.XMLConfigBuilder解析配置的XML文件,将配置参数存入org.apache.ibatis.session.Configuration对象中。

代码分析:

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

将mybatis-config.xml配置文件读取成为输入流对象

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

进入build方法,参数是读取的配置文件流对象

// 这是SqlSessionFactoryBuilder直接调用的build方法,返回值调用其重载方法
public SqlSessionFactory build(InputStream inputStream) {
      return this.build((InputStream)inputStream, (String)null, (Properties)null);
}

public SqlSessionFactory build(InputStream inputStream, String environment) {
      return this.build((InputStream)inputStream, environment, (Properties)null);
}

public SqlSessionFactory build(InputStream inputStream, Properties properties) {
      return this.build((InputStream)inputStream, (String)null, properties);
}
// 
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      SqlSessionFactory var5;
      try {
          // 获取XMLConfigBuilder对象parser 
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          // 最终build方法会返回一个SqlSessionFactory对象var5
          var5 = this.build(parser.parse());
      } catch (Exception var14) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
      } finally {
          ErrorContext.instance().reset();
          try {
            inputStream.close();
          } catch (IOException var13) {
        }
      }
        return var5;
 }

下面我们介绍parser.parse()这步(其实就是对XML的解析过程,解析过之后得到Configuration对象作为参数传给build方法,从而构建SqlSessionFactory工厂),XMLConfigBuilder类用于解析配置文件,继承于BaseBuilder类,以下是截取自XMLConfigBuilder类的部分代码:

// 进入parse()方法
public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            // XMLConfigBuilder解析顶层节点"/configuration"
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            // 返回configuration对象
            return this.configuration;
        }
    }
// 接着调用parseConfiguration(XNode root)方法
private void parseConfiguration(XNode root) {
        try {
            Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
            this.propertiesElement(root.evalNode("properties"));
            this.loadCustomVfs(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 以"typeHandlers"子节点的解析过程为例:配置的typeHandler被注册到typeHandlerRegistry对象中,其定义在BaseBuider类中
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

可以看出通过层层解析,提取配置文件的节点,并将其中的内容注入Configuration对象的各个属性中,供全局调用

public abstract class BaseBuilder {
    protected final Configuration configuration;
    protected final TypeAliasRegistry typeAliasRegistry;
    protected final TypeHandlerRegistry typeHandlerRegistry;

    public BaseBuilder(Configuration configuration) {
        this.configuration = configuration;
        this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
    }
}

通过BaseBuilder类的源码可以得知:typeHandlerRegistry其实是Configuration 单例的一个属性,可以通过Configuration 单例拿到typeHandlerRegistry,从而拿到配置文件中typeHandlers,其他属性以此类推。

在这里我们需要明确的是,构建Configuration对象才是构建SqlSessionFactory的难点和重点,因此我们需要了解一下其构成:

Configuration对象:

作用:

  • 读取基础配置XML和映射器XML
  • 初始化基础配置(别名、重要的类对象(插件、映射器、对象等))
  • 提供单例

属性:properties(属性),settings(设置),typeAliases(类型别名),typeHandlers(类型处理器),objectFactory(对象工厂),mappers(映射器),environments(数据库环境),Plugins(插件)等

2.使用Configuration对象去创建SqlSessiomFactory

public SqlSessionFactory build(Configuration config) {
	//创建了DefaultSqlSessionFactory对象,传入Configuration对象
    return new DefaultSqlSessionFactory(config);
  }

2.SqlSession运行过程

SqlSession是Mybatis的核心接口,其运行过程较第一步更加难以理解

SqlSession session = sqlSessionFactory.openSession();

调用openSession()方法得到SqlSession对象

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

mapper是如何获取并通过动态代理调用方法返回POJO的?下面就源码分析下SqlSession每一步运行的操作:

DefaultSqlSession是SqlSession接口的默认实现类,截取代码片段如下:

public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;
    private Executor executor;
    private boolean autoCommit;
    private boolean dirty;
    private List<Cursor<?>> cursorList;

    public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
    }

    public DefaultSqlSession(Configuration configuration, Executor executor) {
        this(configuration, executor, false);
    }
    public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }
}

继续调用configuration的getMapper方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

继续调用mapperRegistry的getMapper方法,下面截取MapperRegistry类的部分代码:

public class MapperRegistry {
    private final Configuration config;
    // knownMapper是一个HashMap:键为class对象,值为MapperProxyFactory对象
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        // 首先判断是否注册一个mapper,如果没有则抛出异常;如果有就启用mapperProxyFactory工厂生成一个代理实例
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
    // 解析到接口时,将接口class对象和代理工厂对象put进knownMappers中
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }
            }
        }
    }
}

下面是MapperProxyFactory类部分源码:

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return this.methodCache;
    }
    // Mapper的映射是通过动态代理实现的,作用是生成动态代理对象,代理的方法放入mapperProxy中
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

下面是MapperProxy部分源码

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) { // 判断Mapper是否是一个类
            try {
                return method.invoke(this, args);
            } catch (Throwable var5) {
                throw ExceptionUtil.unwrapThrowable(var5);
            }
        } else {
            // 通过cachedMapperMethod方法初始化生成MapperMethod对象
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            // 执行execute方法,将sqlSession和当前运行的数据传进去
            return mapperMethod.execute(this.sqlSession, args);
        }
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }
}

下面截取MapperMethod类的部分代码:

public class MapperMethod {
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }

    public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }
}

我们以result = this.executeForMany(sqlSession, args)为例

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        Object param = this.method.convertArgsToSqlCommandParam(args);
        List result;
        // 先判断有无分页
        if (this.method.hasRowBounds()) {
            RowBounds rowBounds = this.method.extractRowBounds(args);
            result = sqlSession.selectList(this.command.getName(), param, rowBounds);
        } else {
            // 调用了sqlSession类的selectList方法
            result = sqlSession.selectList(this.command.getName(), param);
        }

        if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
            return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
        } else {
            return result;
        }
    }

调用SqlSession的selectList方法

public <E> List<E> selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
    }
    
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }
        return var5;
    }

进入executor.query方法

//此方法在SimpleExecutor的父类BaseExecutor中实现
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
	//根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示
    BoundSql boundSql = ms.getBoundSql(parameter);
    //为本次查询创建缓存的Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
 
//进入query的重载方法中
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
      	// 如果缓存中没有本次查找的值,那么从数据库中查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

//从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 查询的方法
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    // 将查询结果放入缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

// SimpleExecutor中实现父类的doQuery抽象方法
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();
      // 传入参数创建StatementHanlder对象来执行查询
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 创建jdbc中的statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // StatementHandler进行处理
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

// 创建Statement的方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //条代码中的getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得连接。
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
//从连接池获得连接的方法
protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    //从连接池获得连接
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }


//进入StatementHandler进行处理的query,StatementHandler中默认的是PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //原生jdbc的执行
    ps.execute();
    //处理结果返回。
    return resultSetHandler.handleResultSets(ps);
}

故事的最后,还是回到了底层的JDBC操作,Mybatis可以说是对底层操作的封装,其中广泛使用了工厂模式和动态代理模式的设计思想,还需要在今后的实践中不断体会。

自知理解的还很肤浅,欢迎大家批评指正~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值