首页 > 其他 > 详细

【MyBatis源码解析】一次插入的过程

时间:2021-01-25 18:57:23      阅读:23      评论:0      收藏:0      [点我收藏+]

【MyBatis源码解析】一次插入的过程

前言

1.以往链接

2.myBatis经典运行流程

希望读者们能将这个基础的流程熟读于心

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

技术分享图片

一、一次插入操作的整体过程

场景驱动,如果用myBatis进行一次简单的插入操作,其流程是怎么样的?

Mapper.insert()
->MapperProxy.invoke()
->MapperMethod.invoke()
->Executor.excute()
->statementHandler.update()
->statment.excute()

之所以是用插入而不用根据代表的select,原因是select有处理数据集映射成对象实例的过程,为避免篇幅过长,此章节不讨论该过程。

二、一次插入

1.Mapper.insert()

上节我们讨论过了,Mapper是代理类,任何执行的方法会进入其Proxy类的invoke()方法。

如果对此不了解的话,可以去复习一下JDK动态代理的内容。

2.MapperProxy.invoke()

场景驱动,当我们执行Mapper.insert()这方法的时候,会进入红框的部分。

技术分享图片

获得在缓存中的MapperMethod,如果缓存中没有就直接new一个mapperMethod

final MapperMethod mapperMethod = cachedMapperMethod(method);

private final Map<Method, MapperMethod> methodCache;

private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }

然后直接MapperMethod.excute()。

注意下,这次excute函数并没有存入proxy作为参数,也就是说,它不用Proxy实例来执行该次插入操作。先不用及,我们先讨论一下MapperMethod是什么东西。

技术分享图片

现在进入到MethodMapper.excute(SqlSession sqlSession, Object[] args)方法中。

看到command.getType()不用想就知道判断现在是crud的哪款。

技术分享图片

调到MethodSignature.convertArgsToSqlCommandParam()。顾名思义,将Args转换为sql命令。

这是一个脏活累活,有兴趣的读者可以自行了解。我们需要知道的是param这个字段已经转换成了sql语句。

那么,我们继续调到sqlSession.insert(SqlCommand.getName(), param),这是回调哦。

DefaultSqlSession.insert(),在update()那里,直接调用了Executor的update(MappedStament, Object)。现在已经出现了Stament字眼了,离真正的数据库插入应该不远了。

随便说一下,wrapCollection(parameter)是一个集合,存放着#{}里面的数,而MappedStatement存放在各种各样的信息,包括和数据库连接的配置信息、MyBatis的配置信息。

@Override
public int insert(String statement, Object parameter) {
  return update(statement, parameter);
}

  @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();
    }
  }

技术分享图片

3.Executor.excute()

如果是启动了二级缓存,该Executor的实现类就是CachingExecutor,否则就是SimpleExecutor。但对于insert而言,都是在SimpleEXecutor里面执行。

好家伙,没有update方法,那应该是在去父类BaseExecutor中。

技术分享图片

逮到!其执行子类doUpdate()

技术分享图片

红框第一行是创建StatementHandler

第二行是创建jdbc的statement,是从数据库连接池获得Connection然后再获得Statement。

第三行是真正去statement执行

下张图,熟悉的preparedStament的读者。

其实是preparedStament的ps = conn.prepareStatement(sql);

和ps.setString(); 之类的,preparedStatement已经准备完毕,等待excute()!

技术分享图片

技术分享图片

插入操作是需要填入参数,所以用PreparedStatement,防止sql注入攻击嘛。

技术分享图片

4.statementHandler.update()

执行完毕,返回熟悉的rows

技术分享图片

三、链路里不太重要的东西

1.MethodMapper

顾名思义,是Method的包装类。

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

SqlCommand,name是..., type是枚举类,用于确定增删改查的。

public static class SqlCommand {

  private final String name;
  private final SqlCommandType type;
  
    
public enum SqlCommandType {
  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

MethodSignature,这个是Method的配置,就是returnType时候啥那些的。

public static class MethodSignature {

  private final boolean returnsMany;
  private final boolean returnsMap;
  private final boolean returnsVoid;
  private final boolean returnsCursor;
  private final boolean returnsOptional;
  private final Class<?> returnType;
  private final String mapKey;
  private final Integer resultHandlerIndex;
  private final Integer rowBoundsIndex;
  private final ParamNameResolver paramNameResolver;

这个MapperMethod与其说是Mapper接口的包装,不如直接说是mapper.xml中的method的内存映射,而原来的Mapper接口是作为MapperProxy的methodCache的key,而value是这MapperMethod,这样java接口就和mapper.xml文件映射起来了。

四、后言

说说链路中我觉得比较重要的东西。

这个Mybatis坏得很呢,创建了Mapper接口,但重来都不用它们的实例,当然,它们是实例也是接口,没有实现类,空空如也。

它们都是有Mapper接口作为工具人,仅仅用来确定调用者调用哪一个方法,然后用有个Map<Method, MapperMethod> 作为JAVA接口与xml文件的映射。

我们知道真正有用的是mapper.xml文件,它们也用这个巧妙的跳过了Mapper接口,同时也给Spring大佬自己的mapper接口实例。

至于Spring怎么将Mapper收入自己的IOC容器,我想应该可以从MapperScan这个注解入手。

下节,我们主要分析MyBatis作为Orm框架最重要的对象映射,怎么将数据库找那个查询获得的ResultSet转换为JOPO。

【MyBatis源码解析】一次插入的过程

原文:https://www.cnblogs.com/zhoujianyi/p/14326482.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!