MyBatis--相关类的介绍

sqlSessionFactory与SqlSession

从表面上看,我们都是通过SqlSession去执行sql语句的。
SqlSession对应着一次数据库的会话,由于数据库会话不是永久的,因此SqlSession的生命周期也不是永久的,每次访问数据库时都需要创建它(当然不是说在SqlSession里只能执行一次sql,可以执行多次,当一旦关闭了SqlSession就需要重新创建它)。
在这里插入图片描述

  • 首先,SqlSessionFactoryBuilder去读取mybatis的配置文件,然后build一个DefaultSqlSessionFactory
  • 当我们获取到SqlSessionFactory之后,就可以通过SqlSessionFactory去获取SqlSession对象,创建SqlSession的地方只有一个,那就是SqlSessionFactoryopenSession方法,创建SqlSession经过以下几个步骤:
    (1)从配置中获取Environment
    (2)从Environment获取DataSource
    (3)从Environment获取TransactionFactory
    (4)从DataSource里获取数据库连接对象Connection
    (5)在取得的数据库连接上创建事务对象Transaction
    (6)创建Executor对象(该对象非常的重要,事实上SqlSession的所有操作都是通过Executor来完成的
    (7)创建SqlSession对象
  • 拿到SqlSession之后,可以调用SqlSession中一系列select...、insert...、update...、delete...方法进行CRUD操作了。

动态代理

在了解下面的MapperProxy之前,我们先来了解一下什么是动态代理。但是在介绍动态代理之前,先介绍一下什么是代理模式,当我们不想访问或者不能直接访问一个对象的时候,就需要用到代理模式,代理模式一般涉及到委托类代理类两个概念。代理类用于为委托类处理一些事务,代理类对象常常与委托类对象相关联。
代理模式可以分为静态代理和动态代理。静态代理需要为每一个委托类实现一个代理类,程序运行之前代理类的.class文件就已经存在了;而动态代理则是在运行时利用反射机制动态生成的。
动态代理的应用:
Spring的AOP、加事务、加权限、加日志
当想要给实现了某个接口的类中的方法加一些额外的处理,比如说加日志、加事务等,可以给这个类创建一个代理,也就是创建一个新的类,这个类不仅包含了原来类方法的功能,而且还在原来的基础上添加了额外处理的新类
动态代理的实现:
首先必须定义一个接口,还要有一个InvocationHandler处理类(将实现接口的类的对象传递给它);再有一个工具类Proxy(代理类,调用它的newInstance()可以产生代理对象,其实它只是一个产生代理对象的工具类);
动态代理的类实现了InvocationHandler接口,定义Object对象为委托类的对象,而实际执行的方法是在invoke()方法中完成的,此时我们不用再关心代理的对象是谁,只需要实现一套逻辑即可。

  • 定义一个Subject接口,主要用于声明真实对象要让代理对象做的事情:
public interface Subject {
 void buy();
}
  • 创建委托类:
public class RealSubject implements Subject {

 @Override
 public void buy() {
  System.out.println("我要买口红");
 }
}
  • 创建动态代理类:
public class DynamicProxy implements InvocationHandler {
 //object为委托类对象
 private Object object;
 
 public DynamicProxy(Object object) {
  this.object = object;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //调用委托类对象的方法
  Object result = method.invoke(object, args);
  return result;
 }
}
  • 测试类:
public class TestProxy {

 public static void main(String[] args) {
 //创建委托类
  Subject subject = new RealSubject();
  Subject dynamicProxy = (Subject) Proxy.newProxyInstance(
    subject.getClass().getClassLoader(), 
    subject.getClass().getInterfaces(), 
    new DynamicProxy(subject));
  dynamicProxy.buy();
 }
}

MapperProxy

在这里插入图片描述
在MyBatis中,通过MapperProxy动态代理dao,也就是说,当我们执行自己写的dao里面的方法的时候,其实是对应的MapperProxy在代理。
通过SqlSessionConfiguration中获取MapperProxy对象。通过动态代理,就可以方便的使用dao接口了。

Excutor

ExcutorSqlSession的关系就像是市长和书记,SqlSession只是个门面,真正干事的是ExcutorSqlSession对数据库的操作都是通过Excutor来完成的,与SqlSession一样,Excutor也是动态创建的:
在这里插入图片描述
如果不开启cache的话,创建的Excutor只是3个基础类型之一,BatchExecutor专门用于执行批量sql操作。ReuseExecutor会重用statement执行sql操作,SimpleExecutor只是简单执行sql
如果开启cache的话(默认是开启的),就会创建CachingExecutor,它以前面创建的Executor作为唯一参数。CachingExecutor在查询数据库之前先查找缓存,若没找到的话调用delegate(就是构造时传入的Executor对象)从数据库查询,并将查询结果存入缓存中。
Executor对象是可以被插件拦截的,如果定义了针对Executor类型的插件,最终生成的Executor对象是被各个插件插入后的代理对象

sql的执行过程

拿到了MapperProxy之后,每个MapperProxy对应一个dao接口,MapperProxy的执行过程:
对被代理对象的访问都会落实到代理者的invoke上来,MapperProxy在执行时会触发此方法,源码如下:

  /**
   * MapperProxy在执行时会触发此方法
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //如果是Object类的方法
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //二话不说,主要交给MapperMethod自己去管
    return mapperMethod.execute(sqlSession, args);
  }

其中的MapperMethod就像是一个分发者,他根据参数和返回值类型选择不同的sqlsession方法来执行,这样mapper对象与sqlsession就真正的关联起来了。又回到了sqlsession,前面提到过,sqlsession只是一个门面,真正发挥作用的是executor,对sqlsession方法的访问最终都会落到executor的相应方法上去。executor分为两大类:一类是CacheExecutor,一类是Executor

  • CacheExecutor
    CacheExecutor有一个重要属性delegate,他保存的是某类普通的Executor,值在构造时传入。执行数据库update操作时,他直接调用delegateupdate方法,执行query方法时先尝试在cache中取值,取不到再调用delegate的查询方法,并将查询结果存入cache中。

  • 普通Executor
    有三类,他们都继承于BaseExecutor,默认是SimpleExecutorBatchExecutor专门用于执行批量sql操作,ReuseExecutor会重用statement执行sql操作,SimpleExecutor只是简单的执行sql没有什么特别的,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交sql;而BatchExecutor重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能更优。
    但是batch也有自己的问题,比如在insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某些情形下是不符合业务要求的。在同一事务中batch模式和simple模式之间无法转换。

StatementHandler

Executor本质上也是一个甩手掌柜,具体的事情是由StatementHandler来完成的。每次创建的StatementHandler都是RoutingStatementHandler,它只是一个分发者,它的一个属性delegate用于指定用哪种具体的StatementHandler,可选的StatementHandler有三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler,选用哪种在mapper配置文件的每个statement里指定,默认的是PreparedStatementHandlerStatementHandler可以被拦截器拦截,和Executor一样,被拦截器拦截后的对象是一个代理对象。由于MyBatis没有实现数据库的物理分页,众多物理分页的实现都是在这个地方使用拦截器实现的。
StatementHandler创建后需要执行一些初始操作,比如statement的开启和参数设置、对于PreparedStatement还需要执行参数的设置操作等,代码如下:

private Statement prepareStatement(StatementHandler handler) throws SQLException {  
    Statement stmt;  
    Connection connection = transaction.getConnection();  
    stmt =handler.prepare(connection);  
    handler.parameterize(stmt);  
    return stmt;  
}

handler.parameterize通过调用ParameterHandlersetParameters完成参数的设置,ParameterHandler随着 StatementHandler的创建而创建,默认的实现是DefaultParameterHandler
ExecutorStatementHandler一样,ParameterHandler也是可以被拦截的。设置参数的完成实际上使用合适的TypeHandler来完成的。BaseStatementHandler的构造方法中有这么一句:

this.boundSql= mappedStatement.getBoundSql(parameterObject);

它触发了sql的解析,在解析sql的过程中,TypeHandler也被决断出来了,决断的原则是根据参数的类型和参数对应的JDBC类型决定使用哪个TypeHandler
参数设置完毕之后,执行数据库操作(update或query),如果是query最后还有个查询结果的处理过程。
结果处理使用ResultSetHandler来完成的,默认的ResultSetHandlerFastResultSetHandler,他在创建StatementHandler时一起被创建。ResultSetHandler也是可以被拦截的,可以编写自己的拦截器改变ResultSetHandler的默认行为。ResultSetHandler内部一条记录一条记录的处理,在处理每条记录的每一列时会调用TypeHandler转换结果,决断TypeHandler使用的是结果参数的属性类型。
感谢并参考:
https://pdai.tech/md/framework/orm-mybatis/mybatis-y-sql-exec.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值