Mybtais源码(九):增删改执行流程

发布时间 2023-03-24 20:34:45作者: 无虑的小猪

  Mybatis源码(七):SQL执行流程中已提到,根据不同的sqlCommandType执行不同类型的SQL,下面来看看调用Mapper接口的新增、修改、删除,Mybatis中做了哪些处理。

1、新增

1 // 新增用户
2 User user = new User();
3 user.setId(105);
4 user.setName("insert");
5 user.setCreateDate(new Date());
6 user.setUpdateDate(new Date());
7 int addUser = mapper.insertUserInfo(user);
8 System.out.println("新增用户结果: " + addUser);

1、创建MapperMethod对象

  MapperMethod对象在Mybatis源码(七):SQL执行流程中已做分析,MapperMethod对象持有SqlCommand、MethodSignature对象,新增时MapperMethod对象详情如下:

  

2、执行新增流程

  MapperMethod#execute() 核心代码段:

1 case INSERT: {
2    // 使用ParamNameResolver处理args数组,将用户传入的实参与指定参数名称关联起来
3     Object param = method.convertArgsToSqlCommandParam(args);
4    // 调用sqlSession.insert方法,rowCountResult方法会根据method字段中记录的方法的返回值类型对结果进行转换
5    result = rowCountResult(sqlSession.insert(command.getName(), param));
6    break;
7  }

2.1、方法参数解析成SQL参数

  param详情如下:

2.2、执行新增操作

  DefaultSqlSession#update() 核心代码
 1 public int update(String statement, Object parameter) {
 2   try {
 3     dirty = true;
 4     MappedStatement ms = configuration.getMappedStatement(statement);
 5     return executor.update(ms, wrapCollection(parameter));
 6   } catch (Exception e) {
 7     throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
 8   } finally {
 9     ErrorContext.instance().reset();
10   }
11 }

1、获取MappedStatement对象

  根据SatementId(接口全限定名 + 方法名) 获取configuration中的MappedStatement对象,MappedStatement对象详情如下:

  

2、执行新增

  CachingExecutor#update() 核心代码

1 public int update(MappedStatement ms, Object parameterObject) throws SQLException {
2   flushCacheIfRequired(ms);
3   return delegate.update(ms, parameterObject);
4 }

  刷新缓存,通过事务缓存管理器TransactionalCacheManager的clear()方法完成缓存的刷新工作。继续执行新增操作,BaseExecutor#update() 核心代码

 1 public int update(MappedStatement ms, Object parameter) throws SQLException {
 2   ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
 3   // 判断当前Executor是否已经关闭
 4   if (closed) {
 5     throw new ExecutorException("Executor was closed.");
 6   }
 7   // 清除一级缓存
 8   clearLocalCache();
 9   // 执行sql语句
10   return doUpdate(ms, parameter);
11 }

  在新增操作执行之前,优先清除一级缓存中内容。继续执行新增操作,SimpleExecutor#doUpdate() 核心代码

 1 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
 2   Statement stmt = null;
 3   try {
 4     Configuration configuration = ms.getConfiguration();
 5     // 新建一个StatementHandler
 6     // 这里看到ResultHandler传入的是null
 7     StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
 8     // 准备语句
 9     stmt = prepareStatement(handler, ms.getStatementLog());
10     return handler.update(stmt);
11   } finally {
12     closeStatement(stmt);
13   }
14 }
1、获取StatementHandler对象

  SatementHandler作为Mybatis核心四大对象之一,持有其他三大核心对象    parameterHandler参数处理器、resultSetHandler结果集处理器、Executor执行器。

2、生成可执行SQL语句

  SatementHandler对象用于创建Statement对象、生成可执行的SQL。创建的Statement对象详情如下:

 

   已经生成可执行的SQL,同时持从数据源中获取的数据库连接connection.

3、执行新增并返回执行结果

  目标SQL语句已生成,准备工作都已经完成,接下来要将数据插入数据库,PreparedStatementHandler#update() 核心伪代码

1 public int update(Statement statement) throws SQLException {
2   PreparedStatement ps = (PreparedStatement) statement;
3   // 原生JDBC操作
4   ps.execute();
5   // 获取执行结果
6   int rows = ps.getUpdateCount();
7   // ...
8   return rows;
9 }

2、修改

1、创建MapperMethod对象

  MapperMethod对象持有SqlCommand、MethodSignature对象,修改时MapperMethod对象详情如下:

2、执行更新流程

  MapperMethod#execute() 核心代码段:

1 // 更新
2 case UPDATE: {
3   Object param = method.convertArgsToSqlCommandParam(args);
4   result = rowCountResult(sqlSession.update(command.getName(), param));
5   break;
6 }

2.1、方法参数解析成SQL参数

  param详情如下:

2.2、执行更新操作

  DefaultSqlSession#update() 核心代码:
 1 public int update(String statement, Object parameter) {
 2   try {
 3     dirty = true;
 4     MappedStatement ms = configuration.getMappedStatement(statement);
 5     return executor.update(ms, wrapCollection(parameter));
 6   } catch (Exception e) {
 7     throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
 8   } finally {
 9     ErrorContext.instance().reset();
10   }
11 }

1、获取MappedStatement对象

  MappedStatement详情如下:

   

  更新与新增流程一样,在执行更新操作之前,优先刷新缓存信息,此处不再赘述流程,可参考新增中的刷新缓存代码逻辑。

2、执行更新操作

  SimpleExecutor#doUpdate() 核心代码

 1 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
 2   Statement stmt = null;
 3   try {
 4     Configuration configuration = ms.getConfiguration();
 5     // 新建一个StatementHandler
 6     // 这里看到ResultHandler传入的是null
 7     StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
 8     // 准备语句
 9     stmt = prepareStatement(handler, ms.getStatementLog());
10     // 执行更新
11     return handler.update(stmt);
12   } finally {
13     closeStatement(stmt);
14   }
15 }
1、获取StatementHandler对象

2、创建PreparedStatement对象,SQL参数解析生成可执行SQL语句

3、执行更新并返回执行结果
  接下来要将数据更新进数据库,PreparedStatementHandler#update() 核心伪代码
1 public int update(Statement statement) throws SQLException {
2   PreparedStatement ps = (PreparedStatement) statement;
3   ps.execute();
4   int rows = ps.getUpdateCount();
5   // ...
6   return rows;
7 }

3、删除

1、创建MapperMethod对象

  MapperMethod对象持有SqlCommand、MethodSignature对象,删除时MapperMethod对象详情如下:

2、执行删除流程

  MapperMethod#execute() 核心代码段:
1 // 删除
2 case DELETE: {
3   Object param = method.convertArgsToSqlCommandParam(args);
4   result = rowCountResult(sqlSession.delete(command.getName(), param));
5   break;
6 }

2.1、方法参数解析成SQL参数

  param详情如下:

2.2、执行删除操作

  DefaultSqlSession#update() 核心代码:
 1 public int update(String statement, Object parameter) {
 2   try {
 3     dirty = true;
 4     MappedStatement ms = configuration.getMappedStatement(statement);
 5     return executor.update(ms, wrapCollection(parameter));
 6   } catch (Exception e) {
 7     throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
 8   } finally {
 9     ErrorContext.instance().reset();
10   }
11 }

1、获取MappedStatement对象

  MappedStatement详情如下:

2、执行删除

  SimpleExecutor#doUpdate() 核心代码

 1 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
 2   Statement stmt = null;
 3   try {
 4     Configuration configuration = ms.getConfiguration();
 5     // 新建一个StatementHandler
 6     // 这里看到ResultHandler传入的是null
 7     StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
 8     // 准备语句
 9     stmt = prepareStatement(handler, ms.getStatementLog());
10     return handler.update(stmt);
11   } finally {
12     closeStatement(stmt);
13   }
14 }
1、获取StatementHandler对象

2、创建PreparedStatement对象,SQL参数解析生成可执行SQL语句

3、执行更新并返回执行结果
  接下来要将数据更新进数据库,PreparedStatementHandler#update() 核心伪代码
1 public int update(Statement statement) throws SQLException {
2   PreparedStatement ps = (PreparedStatement) statement;
3   ps.execute();
4   int rows = ps.getUpdateCount();
5   // ...
6   return rows;
7 }

4、总结

  Mybtais中增删改执行的是一套流程,只不过对象MappedStatement、Statemnt中的某些属性不同,比如BoundSql、StatementId等,生成的目标执行sql不同,核心对象的创建是一样。

1、创建MapperMethod对象

  创建MapperMethod对象,包含SQL相关信息(接口全限定名+方法名 -> statementId、sql语句类型)、接口方法信息(返回标识、返回类型、参数名称解析器);

2、获取MappedMethod信息

  根据接口全限定名+方法名,获取configuraton中的MappedMethod信息,包含StatementType类型,默认为PREPARED;

3、刷新缓存信息

4、Statement对象获取

  创建StatementHandler对象,在初始化StatementHandler对象时,创建参数处理器ParameterHandelr、结果集处理器resultSetHandler。

  StatementHandler的作用:用于创建statement对象、解析SQL中的占位符,生成可执行SQL。

  statement对象:持有数据库连接、可执行SQL

5、执行SQL语句

  使用PreparedStatement的execute方法执行SQL,原JDBC流程;

6、结果集的处理。