目录
之前的文章写过一篇自定义持久层框架,实现了Mybatis相似的功能。如果想快速掌握mybatis,强烈推荐简单阅读一下这篇文章。
源码地址
https://gitee.com/li_gang_x/my-project-model
有了上面的基础,阅读mybatis源码就不那么陌生了,你会有些许熟悉,似曾相识的感觉。下面我将带领读者进入到mybatis框架,领略大佬们是怎么实现这个持久层框架的,怎么优雅的实现。废话不多说,下面进入主题。

要想 整体把握一个框架,不管要属性怎么使用,而且要知其所以然。整体把握一个框架,首先就要对框架整体有一个认知。

- SqlSession
作为MyBatis ⼯作的主要顶层 API ,表示和数据库交互的会话,完成必要数据库增删改查功能
- Executor
MyBatis执⾏器,是 MyBatis 调度的核⼼,负责 SQL 语句的⽣成和查询缓存的维护
- StatementHandler
封装了JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数、将Statement 结果集转换成List 集合。
- ParameterHandler
负责对⽤户传递的参数转换成JDBC Statement 所需要的参数,
- ResultSetHandler
负责将JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合;
- TypeHandler
负责java 数据类型和 jdbc 数据类型之间的映射和转换
- MappedStatement
MappedStatement维护了⼀条< select | update | delete | insert >节点的封装
- SqlSource
负责根据用户传递的parameterObject,动态生成Sql语句,将信息封装到BoundSql对象中并返回
- BoundSql
表示动态⽣成的SQL 语句以及相应的参数信息
- 加载配置并初始化
- 接收调⽤请求
- 处理操作请求
- 返回处理结果
- public class SqlSessionFactoryBuilder {
-
- //SqlSessionFactoryBuilder有9个build()方法
- //发现mybatis文档老了,http://www.mybatis.org/core/java-api.html,关于这块对不上
-
- //以下3个方法都是调用下面第4种方法
- public SqlSessionFactory build(Reader reader) {
- return build(reader, null, null);
- }
-
- public SqlSessionFactory build(Reader reader, String environment) {
- return build(reader, environment, null);
- }
-
- public SqlSessionFactory build(Reader reader, Properties properties) {
- return build(reader, null, properties);
- }
-
- //第4种方法是最常用的,它使用了一个参照了XML文档或更特定的SqlMapConfig.xml文件的Reader实例。
- //可选的参数是environment和properties。Environment决定加载哪种环境(开发环境/生产环境),包括数据源和事务管理器。
- //如果使用properties,那么就会加载那些properties(属性配置文件),那些属性可以用${propName}语法形式多次用在配置文件中。和Spring很像,一个思想?
- public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
- try {
- //委托XMLConfigBuilder来解析xml文件,并构建
- XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
- return build(parser.parse());
- } catch (Exception e) {
- //这里是捕获异常,包装成自己的异常并抛出的idiom?,最后还要reset ErrorContext
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally {
- ErrorContext.instance().reset();
- try {
- reader.close();
- } catch (IOException e) {
- // Intentionally ignore. Prefer previous error.
- }
- }
- }
-
- //以下3个方法都是调用下面第8种方法
- public SqlSessionFactory build(InputStream inputStream) {
- return build(inputStream, null, null);
- }
-
- public SqlSessionFactory build(InputStream inputStream, String environment) {
- return build(inputStream, environment, null);
- }
-
- public SqlSessionFactory build(InputStream inputStream, Properties properties) {
- return build(inputStream, null, properties);
- }
-
- //第8种方法和第4种方法差不多,Reader换成了InputStream
- public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
- try {
- XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
- return build(parser.parse());
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally {
- ErrorContext.instance().reset();
- try {
- inputStream.close();
- } catch (IOException e) {
- // Intentionally ignore. Prefer previous error.
- }
- }
- }
-
- //最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
- public SqlSessionFactory build(Configuration config) {
- return new DefaultSqlSessionFactory(config);
- }
-
- }
有通过Reader获取和通过Io流获取的两种方法,下面主要写通过输入流获取的方法
- Inputstream inputstream = Resources.getResourceAsStream("mybatisconfig.xml");
- //这⼀⾏代码正是初始化⼯作的开始。
- SqlSessionFactory factory = new
- SqlSessionFactoryBuilder().build(inputStream);
- //第8种方法和第4种方法差不多,Reader换成了InputStream
- public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
- try {
- XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
- return build(parser.parse());
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally {
- ErrorContext.instance().reset();
- try {
- inputStream.close();
- } catch (IOException e) {
- // Intentionally ignore. Prefer previous error.
- }
- }
- }
- //最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
- public SqlSessionFactory build(Configuration config) {
- return new DefaultSqlSessionFactory(config);
- }
Configuration 对象的结构和 xml 配置⽂件的对象⼏乎相同。回顾⼀下 xml 中的配置标签有哪些:properties ( 属性 ) , settings ( 设置 ) , typeAliases ( 类型别名 ) , typeHandlers ( 类型处理器 ) , objectFactory ( 对象⼯⼚ ) , mappers ( 映射器 ) 等 Configuration 也有对应的对象属性来封装它们也就是说,初始化配置⽂件信息的本质就是创建 Configuration 对象,将解析的 xml 数据封装到Configuration 内部属性中
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration>
- <environments default="development">
- <environment id="development">
- <transactionManager type="JDBC"/>
- <dataSource type="POOLED">
- <property name="driver" value="${driver}"/>
- <property name="url" value="${url}"/>
- <property name="username" value="${username}"/>
- <property name="password" value="${password}"/>
- </dataSource>
- </environment>
- </environments>
- <mappers>
- <mapper resource="org/mybatis/example/BlogMapper.xml"/>
- </mappers>
- </configuration>
- //解析配置
- public Configuration parse() {
- //如果已经解析过了,报错
- if (parsed) {
- throw new BuilderException("Each XMLConfigBuilder can only be used once.");
- }
- parsed = true;
-
-
- //根节点是configuration
- parseConfiguration(parser.evalNode("/configuration"));
- return configuration;
- }
- //解析配置
- private void parseConfiguration(XNode root) {
- try {
- //分步骤解析
- //issue #117 read properties first
- //1.properties
- propertiesElement(root.evalNode("properties"));
- //2.类型别名
- typeAliasesElement(root.evalNode("typeAliases"));
- //3.插件
- pluginElement(root.evalNode("plugins"));
- //4.对象工厂
- objectFactoryElement(root.evalNode("objectFactory"));
- //5.对象包装工厂
- objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
- //6.设置
- settingsElement(root.evalNode("settings"));
- // read it after objectFactory and objectWrapperFactory issue #631
- //7.环境
- environmentsElement(root.evalNode("environments"));
- //8.databaseIdProvider
- databaseIdProviderElement(root.evalNode("databaseIdProvider"));
- //9.类型处理器
- typeHandlerElement(root.evalNode("typeHandlers"));
- //10.映射器
- mapperElement(root.evalNode("mappers"));
- } catch (Exception e) {
- throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
- }
- }
- <select id="getUser" resultType="user" >
- select * from user where id=#{id}
- </select>
这样的⼀个select标签会在初始化配置⽂件时被解析封装成⼀个MappedStatement对象,然后存储在 Configuration对象的mappedStatements属性中,mappedStatements 是⼀个HashMap,存储时
key =全限定类名+⽅法名,
value =对应的MappedStatement对象。
Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement> ("Mapped Statements collection")
- private void parseConfiguration(XNode root) {
- try {
- //省略其他标签的处理
- mapperElement(root.evalNode("mappers"));
- } catch (Exception e) {
- throw new BuilderException("Error parsing SQL Mapper Configuration.Cause:" + e, e);
- }
- }
- // 5.调⽤的重载⽅法
- public SqlSessionFactory build(Configuration config) {
- //创建了 DefaultSqlSessionFactory 对象,传⼊ Configuration 对象。
- return new DefaultSqlSessionFactory(config);
- }
- public class DefaultSqlSession implements SqlSession {
-
- private Configuration configuration;
- private Executor executor;
- ...
- }
- SqlSession sqlSession = factory.openSession();
- List<User> list =
- sqlSession.selectList("com.lagou.mapper.UserMapper.getUserByName");
进入到openSession方法
- //6. 进⼊ openSession ⽅法。
- public SqlSession openSession() {
- //getDefaultExecutorType()传递的是SimpleExecutor
- return openSessionFromDataSource(configuration.getDefaultExecutorType(), null,
- false);
- }
可以看到调用的是openSessionFormDataSource
- 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);
- //生成一个执行器(事务包含在执行器里)
- final Executor executor = configuration.newExecutor(tx, execType);
- //然后产生一个DefaultSqlSession
- 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();
- }
- }
- //8.进⼊selectList⽅法,多个重载⽅法
- @Override
- public <E> List<E> selectList(String statement) {
- return this.selectList(statement, null);
- }
-
- @Override
- public <E> List<E> selectList(String statement, Object parameter) {
- return this.selectList(statement, parameter, RowBounds.DEFAULT);
- }
-
- //核心selectList
- @Override
- public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
- try {
- //根据statementId(全限定名+⽅法名)找到对应的MappedStatement 根据传⼊的全限定名+⽅法名从映射的Map中取出MappedStatement对象
- MappedStatement ms = configuration.getMappedStatement(statement);
- //调⽤Executor中的⽅法处理
- //转而用执行器来查询结果,注意这里传入的ResultHandler是null
- //RowBounds是⽤来逻辑分⻚
- // wrapCollection(parameter)是⽤来装饰集合或者数组参数
- 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();
- }
- }
- //SqlSession.selectList会调用此方法
- // 继续源码中的步骤,进⼊executor.query()
- @Override
- public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
- //根据传⼊的参数动态获得SQL语句,最后返回⽤BoundSql对象表示
- BoundSql boundSql = ms.getBoundSql(parameter);
- //创建缓存Key 为本次查询创建缓存的Key
- CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
- //查询
- return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
- }
- @Override
- 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.");
- }
- //先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
- if (queryStack == 0 && ms.isFlushCacheRequired()) {
- clearLocalCache();
- }
- List<E> list;
- try {
- //加一,这样递归调用到上面的时候就不会再清局部缓存了
- queryStack++;
- //先根据cachekey从localCache去查
- list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
- if (list != null) {
- //若查到localCache缓存,处理localOutputParameterCache
- 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
- //如果是STATEMENT,清本地缓存
- 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);
- //如果是存储过程,OUT参数也加入缓存
- if (ms.getStatementType() == StatementType.CALLABLE) {
- localOutputParameterCache.putObject(key, parameter);
- }
- return list;
- }
这里有个doQuery 方法阅读过源码的同学都知道,do开头的一般是真正执行逻辑的方法
- //query-->queryFromDatabase-->doQuery
- protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
- throws SQLException;
这个方法是protected修饰的,是子类实现
- //select
- @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
- //这里看到ResultHandler传入了
- //传⼊参数创建StatementHandler对象来执⾏查询
- StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
- //准备语句 创建jdbc中的statement对象
- stmt = prepareStatement(handler, ms.getStatementLog());
- //StatementHandler.query
- return handler.<E>query(stmt, resultHandler);
- } finally {
- closeStatement(stmt);
- }
- }
下面查看prepareStatement方法的实现
- private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
- Statement stmt;
- //代码中的getConnection⽅法经过重重调⽤最后会调⽤openConnection⽅法,从连接池中获得连接
- Connection connection = getConnection(statementLog);
- //调用StatementHandler.prepare
- stmt = handler.prepare(connection);
- //调用StatementHandler.parameterize
- handler.parameterize(stmt);
- return stmt;
- }
这里的连接是 JdbcTransaction 中从连接池中获取的连接
- /**
- * 从连接池获得连接的⽅法
- * @throws SQLException
- */
- protected void openConnection() throws SQLException {
- if (log.isDebugEnabled()) {
- log.debug("Opening JDBC Connection");
- }
- //从连接池获得连接
- connection = dataSource.getConnection();
- if (level != null) {
- connection.setTransactionIsolation(level.getLevel());
- }
- setDesiredAutoCommit(autoCommmit);
- }
上述的Executor.query()⽅法⼏经转折,最后会创建⼀个StatementHandler对象,然后将必要的参数传递给 StatementHandler,使⽤StatementHandler来完成对数据库的查询,最终返回List结果集。
(1 、根据传递的参数,完成 SQL 语句的动态解析,⽣成 BoundSql 对象,供 StatementHandler 使⽤;(2 、为查询创建缓存,以提⾼性能(3 、创建 JDBC 的 Statement 连接对象,传递给 *StatementHandler* 对象,返回 List 查询结果
进⼊到 StatementHandler 的 parameterize(statement)⽅法的实现:
- //参数化
- void parameterize(Statement statement)
- throws SQLException;
这里将参数进行替换设置
这个接口的实现是
PreparedStatementHandler 预处理语句处理器(PREPARED)
- @Override
- public void parameterize(Statement statement) throws SQLException {
- //调用ParameterHandler.setParameters
- parameterHandler.setParameters((PreparedStatement) statement);
- }
下面是设置参数的具体实现
- //设置参数
- /** ParameterHandler 类的 setParameters(PreparedStatement ps) 实现
- * 对某⼀个Statement进⾏设置参数
- * */
- @Override
- public void setParameters(PreparedStatement ps) throws SQLException {
- ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
- List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
- if (parameterMappings != null) {
- //循环设参数
- for (int i = 0; i < parameterMappings.size(); i++) {
- ParameterMapping parameterMapping = parameterMappings.get(i);
- if (parameterMapping.getMode() != ParameterMode.OUT) {
- //如果不是OUT,才设进去
- Object value;
- String propertyName = parameterMapping.getProperty();
- if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
- //若有额外的参数, 设为额外的参数
- value = boundSql.getAdditionalParameter(propertyName);
- } else if (parameterObject == null) {
- //若参数为null,直接设null
- value = null;
- } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
- //若参数有相应的TypeHandler,直接设object
- value = parameterObject;
- } else {
- //除此以外,MetaObject.getValue反射取得值设进去
- MetaObject metaObject = configuration.newMetaObject(parameterObject);
- value = metaObject.getValue(propertyName);
- }
- // 每⼀个 Mapping都有⼀个 TypeHandler,根据 TypeHandler 来对preparedStatement 进⾏设置参数
- TypeHandler typeHandler = parameterMapping.getTypeHandler();
- JdbcType jdbcType = parameterMapping.getJdbcType();
- if (value == null && jdbcType == null) {
- //不同类型的set方法不同,所以委派给子类的setParameter方法
- jdbcType = configuration.getJdbcTypeForNull();
- }
- //设置参数
- typeHandler.setParameter(ps, i + 1, value, jdbcType);
- }
- }
- }
- }
- @Override
- public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
- // 1.调⽤preparedStatemnt。execute()⽅法,然后将resultSet交给ResultSetHandler处理
- PreparedStatement ps = (PreparedStatement) statement;
- ps.execute();
- //2.使⽤ ResultHandler 来处理 ResultSet
- return resultSetHandler.<E> handleResultSets(ps);
- }
也是PreparedStatementHandler实现的方法
- @Override
- public List<Object> handleResultSets(Statement stmt) throws SQLException {
- ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
- //多ResultSet的结果集合,每个ResultSet对应⼀个Object对象。
- // ⽽实际上,每 个 Object 是List<Object> 对象。
- //在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,
- // 也就是说,multipleResults最多就⼀个元素。
- final List<Object> multipleResults = new ArrayList<Object>();
-
- int resultSetCount = 0;
- //获得⾸个ResultSet对象,并封装成ResultSetWrapper对象
- ResultSetWrapper rsw = getFirstResultSet(stmt);
- //获得ResultMap数组
- //在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,
- // 也就是说,resultMaps就⼀个元素。
- List<ResultMap> resultMaps = mappedStatement.getResultMaps();
- //一般resultMaps里只有一个元素
- int resultMapCount = resultMaps.size();
- validateResultMapsCount(rsw, resultMapCount);
- while (rsw != null && resultMapCount > resultSetCount) {
- //获得ResultMap对象
- ResultMap resultMap = resultMaps.get(resultSetCount);
- //处理ResultSet,将结果添加到multipleResults中
- handleResultSet(rsw, resultMap, multipleResults, null);
- //获得下⼀个ResultSet对象,并封装成ResultSetWrapper对象
- rsw = getNextResultSet(stmt);
- //清理
- cleanUpAfterHandlingResultSet();
- resultSetCount++;
- }
- //因为'mappedStatement.resultSets'只在存储过程中使⽤,本系列暂时不考虑,忽略即可
- String[] resultSets = mappedStatement.getResulSets();
- if (resultSets != null) {
- while (rsw != null && resultSetCount < resultSets.length) {
- ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
- if (parentMapping != null) {
- String nestedResultMapId = parentMapping.getNestedResultMapId();
- ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
- handleResultSet(rsw, resultMap, null, parentMapping);
- }
- rsw = getNextResultSet(stmt);
- cleanUpAfterHandlingResultSet();
- resultSetCount++;
- }
- }
- //如果是multipleResults单元素,则取⾸元素返回
- return collapseSingleResultList(multipleResults);
- }
- public static void main(String[] args) {
- //前三步都相同
- InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
- SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
- SqlSession sqlSession = factory.openSession();
- //这⾥不再调⽤SqlSession的api,⽽是获得了接⼝对象,调⽤接⼝中的⽅法。
- UserMapper mapper = sqlSession.getMapper(UserMapper.class);
- List<User> list = mapper.getUserByName("tom");
- }
- <mapper class="com.lagou.mapper.UserMapper"/>
- <package name="com.lagou.mapper"/>
- </mappers>
映射器注册机
DefaultSqlSession默认的sqlSession
- @Override
- public <T> T getMapper(Class<T> type) {
- //最后会去调用MapperRegistry.getMapper
- return configuration.<T>getMapper(type, this);
- }
调用的是Configuration中的getMapper
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- return mapperRegistry.getMapper(type, sqlSession);
- }
最终调用的是MapperRegistry 中的 getMapper
- //返回代理类
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- //从 MapperRegistry 中的 HashMap 中拿 MapperProxyFactory
- final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
- if (mapperProxyFactory == null) {
- throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
- }
- try {
- //通过动态代理⼯⼚⽣成示例
- return mapperProxyFactory.newInstance(sqlSession);
- } catch (Exception e) {
- throw new BindingException("Error getting mapper instance. Cause: " + e, e);
- }
- }
这里的newInstance 方法如下
MapperProxyFactory
- public T newInstance(SqlSession sqlSession) {
- //创建了 JDK动态代理的Handler类
- final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
- //调⽤了重载⽅法
- return newInstance(mapperProxy);
- }
下面是调用的JDK的动态代理
- protected T newInstance(MapperProxy<T> mapperProxy) {
- //用JDK自带的动态代理生成映射器
- return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, 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;
- //构造,传⼊了 SqlSession,说明每个session中的代理对象的不同的!
- public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
- this.sqlSession = sqlSession;
- this.mapperInterface = mapperInterface;
- this.methodCache = methodCache;
- }
- ...省略部分源码,剩余的在下面
-
- }
- //如果是Object定义的⽅法,直接调⽤
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
- //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
- if (Object.class.equals(method.getDeclaringClass())) {
- try {
- return method.invoke(this, args);
- } catch (Throwable t) {
- throw ExceptionUtil.unwrapThrowable(t);
- }
- }
- //这里优化了,去缓存中找MapperMethod
- final MapperMethod mapperMethod = cachedMapperMethod(method);
- //重点在这:MapperMethod最终调⽤了执⾏的⽅法
- return mapperMethod.execute(sqlSession, args);
- }
MapperMethod最终调⽤了 mapperMethod.execute(sqlSession, args); 执⾏的⽅法
- /**
- * 映射器代理,代理模式
- *
- */
- 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;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
- //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
- if (Object.class.equals(method.getDeclaringClass())) {
- try {
- return method.invoke(this, args);
- } catch (Throwable t) {
- throw ExceptionUtil.unwrapThrowable(t);
- }
- }
- //这里优化了,去缓存中找MapperMethod
- final MapperMethod mapperMethod = cachedMapperMethod(method);
- //执行
- return mapperMethod.execute(sqlSession, args);
- }
-
- //去缓存中找MapperMethod
- private MapperMethod cachedMapperMethod(Method method) {
- MapperMethod mapperMethod = methodCache.get(method);
- if (mapperMethod == null) {
- //找不到才去new
- mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
- methodCache.put(method, mapperMethod);
- }
- return mapperMethod;
- }
-
- }
MapperMethod进⼊execute⽅法:
方法类别的判断方法,在构造方法中初始化
- private final SqlCommand command;
- private final MethodSignature method;
-
- public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
- this.command = new SqlCommand(config, mapperInterface, method);
- this.method = new MethodSignature(config, method);
- }
- //执行
- public Object execute(SqlSession sqlSession, Object[] args) {
- Object result;
- //可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
- //判断mapper中的⽅法类型,最终调⽤的还是SqlSession中的⽅法 switch(command.getType()) {
-
-
- if (SqlCommandType.INSERT == command.getType()) {
- //转换参数
- Object param = method.convertArgsToSqlCommandParam(args);
- //执⾏INSERT操作
- // 转换 rowCount
- result = rowCountResult(sqlSession.insert(command.getName(), param));
- } else if (SqlCommandType.UPDATE == command.getType()) {
- //转换参数
- Object param = method.convertArgsToSqlCommandParam(args);
- // 转换 rowCount
- result = rowCountResult(sqlSession.update(command.getName(), param));
- } else if (SqlCommandType.DELETE == command.getType()) {
- //转换参数
- Object param = method.convertArgsToSqlCommandParam(args);
- // 转换 rowCount
- result = rowCountResult(sqlSession.delete(command.getName(), param));
- //⽆返回,并且有ResultHandler⽅法参数,则将查询的结果,提交给 ResultHandler 进⾏处理
- } else if (SqlCommandType.SELECT == command.getType()) {
- if (method.returnsVoid() && method.hasResultHandler()) {
- //如果有结果处理器
- executeWithResultHandler(sqlSession, args);
- result = null;
- //执⾏查询,返回列表
- } else if (method.returnsMany()) {
- //如果结果有多条记录
- result = executeForMany(sqlSession, args);
- //执⾏查询,返回Map
- } else if (method.returnsMap()) {
- //如果结果是map
- result = executeForMap(sqlSession, args);
- } else {
- //否则就是一条记录 查询单条
- Object param = method.convertArgsToSqlCommandParam(args);
- result = sqlSession.selectOne(command.getName(), param);
- }
- } else {
- throw new BindingException("Unknown execution method for: " + command.getName());
- }
- //返回结果为null,并且返回类型为基本类型,则抛出BindingException异常
- 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;
- }
-
- //这个方法对返回值的类型进行了一些检查,使得更安全
- private Object rowCountResult(int rowCount) {
- final Object result;
- if (method.returnsVoid()) {
- result = null;
- } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
- //如果返回值是大int或小int
- result = Integer.valueOf(rowCount);
- } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
- //如果返回值是大long或小long
- result = Long.valueOf(rowCount);
- } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
- //如果返回值是大boolean或小boolean
- result = Boolean.valueOf(rowCount > 0);
- } else {
- throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
- }
- return result;
- }
下面是参数转换成sql 语句中的参数
- public Object convertArgsToSqlCommandParam(Object[] args) {
- final int paramCount = params.size();
- if (args == null || paramCount == 0) {
- //如果没参数
- return null;
- } else if (!hasNamedParameters && paramCount == 1) {
- //如果只有一个参数
- return args[params.keySet().iterator().next().intValue()];
- } else {
- //否则,返回一个ParamMap,修改参数名,参数名就是其位置
- final Map<String, Object> param = new ParamMap<Object>();
- int i = 0;
- for (Map.Entry<Integer, String> entry : params.entrySet()) {
- //1.先加一个#{0},#{1},#{2}...参数
- param.put(entry.getValue(), args[entry.getKey().intValue()]);
- // issue #71, add param names as param1, param2...but ensure backward compatibility
- final String genericParamName = "param" + String.valueOf(i + 1);
- if (!param.containsKey(genericParamName)) {
- //2.再加一个#{param1},#{param2}...参数
- //你可以传递多个参数给一个映射器方法。如果你这样做了,
- //默认情况下它们将会以它们在参数列表中的位置来命名,比如:#{param1},#{param2}等。
- //如果你想改变参数的名称(只在多参数情况下) ,那么你可以在参数上使用@Param(“paramName”)注解。
- param.put(genericParamName, args[entry.getKey()]);
- }
- i++;
- }
- return param;
- }
- }
好了Mybatis执行流程就讲到这了。后面会对部分进细节行更加详细的讲述
如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。