Mybatis-plus 源码执行全流程解析

Mybatis-plus 源码执行全流程解析

废话少说,流程开始:

1、业务入口:userMapper.insertBatch(entityList);

执行到代理类: $Proxy222 (com.sun.proxy) 开始走代理流程,然后到了代理类:

idea 执行流程取出栈信息:

invoke:89, MybatisMapperProxy (com.baomidou.mybatisplus.core.override)

提前生成sqlSession ,地方如下:

2、代理类:public class MybatisMapperProxy implements InvocationHandler, Serializable

kotlin 复制代码
    // 当前代理类、方法名、保存的参数对象,可能多个
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else {//核心接口进入,带着生成的sqlSession
                return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }

来到:

php 复制代码
// private final Map<Method, MapperMethodInvoker> methodCache; 类似缓存操作
 private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {// 集合的操作方法,通过接口回调返回数据
            return CollectionUtils.computeIfAbsent(methodCache, method, m -> {
                if (m.isDefault()) {
                    try {
                        if (privateLookupInMethod == null) {
                            return new DefaultMethodInvoker(getMethodHandleJava8(method));
                        } else {
                            return new DefaultMethodInvoker(getMethodHandleJava9(method));
                        }
                    } catch (IllegalAccessException | InstantiationException | InvocationTargetException
                        | NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException re) {
            Throwable cause = re.getCause();
            throw cause == null ? re : cause;
        }
    }

进入新的invoke 方法:

typescript 复制代码
        @Override
        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
            //private final MybatisMapperMethod mapperMethod 方法名对象
            return mapperMethod.execute(sqlSession, args);//核心操作
        }

3、来到 public class MybatisMapperMethod 类:

ini 复制代码
// private final MapperMethod.MethodSignature method ;方法的类名
public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {//MapperMethod.SqlCommand command; 对象
            case INSERT: {//新增会走这里,点击进入,合成参数数据,进入
                Object param = method.convertArgsToSqlCommandParam(args);
                //通过方法名、参数,执行并返回执行结果,进入
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
            }
            case UPDATE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
            }
            case DELETE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
            }
            case SELECT:
                if (method.returnsVoid() && method.hasResultHandler()) {
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (method.returnsMany()) {
                    result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                    result = executeForCursor(sqlSession, args);
                } else {
                    Object param = method.convertArgsToSqlCommandParam(args);
                    // TODO 这里下面改了
                    if (IPage.class.isAssignableFrom(method.getReturnType())) {
                        result = executeForIPage(sqlSession, args);
                        // TODO 这里上面改了
                    } else {
                        result = sqlSession.selectOne(command.getName(), param);
                        if (method.returnsOptional()
                            && (result == null || !method.getReturnType().equals(result.getClass()))) {
                            result = Optional.ofNullable(result);
                        }
                    }
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
        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;
    }

4、来到 public class MapperMethod 的内部类MethodSignature

typescript 复制代码
   //private final ParamNameResolver paramNameResolver; 参数分解器
   public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);//进入
    }

5、来到:public class ParamNameResolver

dart 复制代码
public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      Object value = args[names.firstKey()];
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

最后返回:

下面重点分析:

ini 复制代码
result = rowCountResult(sqlSession.insert(command.getName(), param));

点击 sqlSession.insert(command.getName(), param) 进入:三个实现类的其中一个

根据上游入口判断进入spring的类:

kotlin 复制代码
public class SqlSessionTemplate implements SqlSession, DisposableBean

6、来到 SqlSessionTemplate

typescript 复制代码
  //private final SqlSession sqlSessionProxy;方法的全路径、执行参数
  @Override
  public int insert(String statement, Object parameter) {
    return this.sqlSessionProxy.insert(statement, parameter);//进入
  }

三个实现类:

来到子类 public class DefaultSqlSession implements SqlSession

7、DefaultSqlSession

typescript 复制代码
  @Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);//进入执行
  }

来到:

typescript 复制代码
 //private final Executor executor;执行器接口
 //private final Configuration configuration; 配置类
  @Override
  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();//实例资源清除
    }
  }

点击configuration.getMappedStatement(statement) 进入

来到 public class MybatisConfiguration extends Configuration 此类为mybatisplus的类,继承的mybatis的配置类Configuration 。

8、MybatisConfiguration 类

typescript 复制代码
   //此id就是mapper的全路径,在此服务中应该也是唯一的值
    @Override
    public MappedStatement getMappedStatement(String id) {
        return this.getMappedStatement(id, true);//正式获取声明
    }

来到:

typescript 复制代码
   //protected final Map<String, MappedStatement> mappedStatements 
    @Override
    public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
        if (validateIncompleteStatements) {
            buildAllStatements();//上游传的true,进入
        }
        return mappedStatements.get(id);//在此处返回
    }

点击: buildAllStatements() 进入父类Configuration,组装的细节后面我们会详细分享

scss 复制代码
protected void buildAllStatements() {
    parsePendingResultMaps();
    if (!incompleteCacheRefs.isEmpty()) {
      synchronized (incompleteCacheRefs) {
        incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null);
      }
    }
    if (!incompleteStatements.isEmpty()) {
      synchronized (incompleteStatements) {
        incompleteStatements.removeIf(x -> {
          x.parseStatementNode();
          return true;
        });
      }
    }
    if (!incompleteMethods.isEmpty()) {
      synchronized (incompleteMethods) {
        incompleteMethods.removeIf(x -> {
          x.resolve();
          return true;
        });
      }
    }
  }

返回后进入 executor.update(ms, wrapCollection(parameter));进入切面类

java 复制代码
public class Plugin implements InvocationHandler
typescript 复制代码
  //切面方法
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        //private final Interceptor interceptor;拦截器接口
        return interceptor.intercept(new Invocation(target, method, args));//进入拦截器
      }
      return method.invoke(target, args);//进行执行业务
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

点击:

来到:public class OptimisticLockerInterceptor implements Interceptor mybatis 中的类

scss 复制代码
@Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        if (SqlCommandType.UPDATE != ms.getSqlCommandType()) {
            return invocation.proceed();
        }
        Object param = args[1];
        if (param instanceof Map) {
            Map map = (Map) param;
            //updateById(et), update(et, wrapper);
            Object et = map.getOrDefault(Constants.ENTITY, null);
            if (et != null) {
                // entity
                String methodId = ms.getId();
                String methodName = methodId.substring(methodId.lastIndexOf(StringPool.DOT) + 1);
                TableInfo tableInfo = TableInfoHelper.getTableInfo(et.getClass());
                if (tableInfo == null || !tableInfo.isWithVersion()) {
                    return invocation.proceed();
                }
                TableFieldInfo fieldInfo = tableInfo.getVersionFieldInfo();
                Field versionField = fieldInfo.getField();
                // 旧的 version 值
                Object originalVersionVal = versionField.get(et);
                if (originalVersionVal == null) {
                    return invocation.proceed();
                }
                String versionColumn = fieldInfo.getColumn();
                // 新的 version 值
                Object updatedVersionVal = this.getUpdatedVersionVal(fieldInfo.getPropertyType(), originalVersionVal);
                if (PARAM_UPDATE_METHOD_NAME.equals(methodName)) {
                    AbstractWrapper<?, ?, ?> aw = (AbstractWrapper<?, ?, ?>) map.getOrDefault(Constants.WRAPPER, null);
                    if (aw == null) {
                        UpdateWrapper<?> uw = new UpdateWrapper<>();
                        uw.eq(versionColumn, originalVersionVal);
                        map.put(Constants.WRAPPER, uw);
                    } else {
                        aw.apply(versionColumn + " = {0}", originalVersionVal);
                    }
                } else {
                    map.put(Constants.MP_OPTLOCK_VERSION_ORIGINAL, originalVersionVal);
                }
                versionField.set(et, updatedVersionVal);
                return invocation.proceed();
            }
        }
        return invocation.proceed();//点击进入
    }

来到:public class Invocation

arduino 复制代码
public Object proceed() throws InvocationTargetException, IllegalAccessException {
  return method.invoke(target, args);//进入
}

到了:public final class Method extends Executable

less 复制代码
    @CallerSensitive
    @ForceInline // to ensure Reflection.getCallerClass optimization
    @HotSpotIntrinsicCandidate
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz,
                        Modifier.isStatic(modifiers) ? null : obj.getClass(),
                        modifiers);
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);//进入
    }

进入:

三个实现类

9、来到 public class MybatisCachingExecutor implements Executor 实现类 mybatisplus包中

java 复制代码
    @Override
    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        flushCacheIfRequired(ms);//刷新清除缓存,下次查询此表,会到数据库查,点击
        // private final Executor delegate;执行器执行
        return delegate.update(ms, parameterObject);//核心执行,点击
    }

来到:

java 复制代码
 private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        if (cache != null && ms.isFlushCacheRequired()) {
            tcm.clear(cache);//清除缓存
        }
    }

点击 delegate.update(ms, parameterObject);

10、到了 public abstract class BaseExecutor implements Executor

scss 复制代码
  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache();//清除缓存
    return doUpdate(ms, parameter);//正式执行
  }

点击:

java 复制代码
  protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

抽象方法的重写:有mybatis和plus的两组实现

10、 到:public class MybatisSimpleExecutor extends AbstractBaseExecutor

java 复制代码
    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);//生成执行的声明,里面很复杂,后期我们很专题讲解
            stmt = prepareStatement(handler, ms.getStatementLog(), false);//拿数据源重点,进入
            return stmt == null ? 0 : handler.update(stmt);//正式执行,点击
        } finally {
            closeStatement(stmt);//关闭声明
        }
    }

点击 prepareStatement(handler, ms.getStatementLog(), false);

ini 复制代码
private Statement prepareStatement(StatementHandler handler, Log statementLog, boolean isCursor) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);//取数据源,进入
        stmt = handler.prepare(connection, transaction.getTimeout());封装数据源到声明里
        //游标不支持返回null.
        if (stmt == null && !isCursor) {
            return null;
        } else {
            handler.parameterize(stmt);
            return stmt;
        }
    }

11、到了父类 public abstract class BaseExecutor implements Executor

java 复制代码
protected Connection getConnection(Log statementLog) throws SQLException {
    // protected Transaction transaction;
    Connection connection = transaction.getConnection();//获取数据源,进入
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

点击发现常用的三种实现:

显然 进入 mybatis-spring 包里的SpringManagedTransaction

12、来到 public class SpringManagedTransaction implements Transaction 类

kotlin 复制代码
 @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();//获取连接,进入
    }
    return this.connection;
  }

点击 openConnection()

kotlin 复制代码
private void openConnection() throws SQLException {
    //通过spring的工具类DataSourceUtils 获取 数据库连接connection(JDK包里的),进入
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();//自动提交的获取
    //是否连接、事务的判断
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

    LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
        + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
  }

来到:

java 复制代码
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);//点击
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
		}
		catch (IllegalStateException ex) {
			throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage());
		}
	}

点击:

scss 复制代码
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				logger.debug("Fetching resumed JDBC Connection from DataSource");
				conHolder.setConnection(fetchConnection(dataSource));
			}
			return conHolder.getConnection();
		}
		// Else we either got no holder or an empty thread-bound holder here.

		logger.debug("Fetching JDBC Connection from DataSource");
		Connection con = fetchConnection(dataSource);//重点进入

		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			try {
				// Use same Connection for further JDBC actions within the transaction.
				// Thread-bound object will get removed by synchronization at transaction completion.
				ConnectionHolder holderToUse = conHolder;
				if (holderToUse == null) {
					holderToUse = new ConnectionHolder(con);
				}
				else {
					holderToUse.setConnection(con);
				}
				holderToUse.requested();
				TransactionSynchronizationManager.registerSynchronization(
						new ConnectionSynchronization(holderToUse, dataSource));
				holderToUse.setSynchronizedWithTransaction(true);
				if (holderToUse != conHolder) {
					TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
				}
			}
			catch (RuntimeException ex) {
				// Unexpected exception from external delegation call -> close Connection and rethrow.
				releaseConnection(con, dataSource);
				throw ex;
			}
		}

		return con;
	}

来到:

java 复制代码
private static Connection fetchConnection(DataSource dataSource) throws SQLException {
		Connection con = dataSource.getConnection();//点击进入,dataSource 的实现类
		if (con == null) {
			throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
		}
		return con;
	}

点击:dataSource.getConnection()发现好的实现类,应该进入哪一个啊??

根据业务判断:进入NandaoDataSource

kotlin 复制代码
public class NandaoDataSource  extends AbstractTenantDataSource implements DataSource, NandaoDataSourceMXBean, MBeanRegistration, AutoCloseable{
java 复制代码
   @Override
    public Connection getConnection() throws SQLException {
        if (provider == null) {
            throw new Exception("provider is null");
        }
        return getConnection(provider.getUserId());//业务封装回去自定义数据源
    }

进入

ini 复制代码
public Connection getConnection(String userId) throws SQLException {

        for (int retry = 1; retry <= retryTimes; retry++) {
            UserDbInfo dbInfo = userDbInfoInfoService.getDatabaseInfo(userId);
            
                return getDataSource(dbInfo).getConnection();
           }
    }

这是获取数据源的流程,分析到此,下篇我们分析真正执行数据库的流程 handler.update(stmt);,敬请期待!

相关推荐
华仔啊6 小时前
提升 Java 开发效率的 5 个神级技巧,超过 90% 的人没用全!
java·后端
间彧6 小时前
Docker Compose如何编排包含数据库、缓存等多个服务的SpringBoot应用?
后端
码农刚子6 小时前
ASP.NET Core Blazor 核心功能一:Blazor依赖注入与状态管理指南
前端·后端
是你的小恐龙啊6 小时前
自动化信息交付:深度解析AI驱动的每日简报系统架构与实现
后端
小码编匠6 小时前
WPF 动态模拟CPU 使用率曲线图
后端·c#·.net
我是谁的程序员6 小时前
让调试成为团队优势,如何把Charles融入前端与测试的工作流
后端
Java水解6 小时前
Spring AI Alibaba 入门教程:快速集成大模型到Spring Boot应用
后端·spring
Java水解6 小时前
Flowable工作流引擎:Spring Boot集成
后端
王中阳Go背后的男人6 小时前
订单支付后库存不扣减,如何用RabbitMQ来优化?
后端