浅析Mybatis核心组件

浅析Mybatis核心组件

前言

Mybaits作为国内大流行的“伪”ORM框架,它也是不少大厂的“御用框架”,可想而知其地位举足轻重。究其原因,国内很多互联网公司早期大部分都是基于面向数据库(表)编程,而java对象只是作为数据的载体,数据模型的CRUD都设计在一张表上,而业务逻辑就是不同表的CRUD集合。所以开发者需要它做的就是将SQL自动封装映射成java对象,没有其他花里胡哨的功能,这也是它流行原因之一。

Mybaits组件简要流程图

浅析Mybatis核心组件

Configuration

浅析Mybatis核心组件

  • Configuration:MyBatis所有的主配置信息都在Configuration里面,MyBatis通过Configuration对象获取各种配置,例如:启动加载的MappedStatement、TypeHandler、类型别名、插件等等。
  • MappedStatement:它封装了就是大家经常编写的XxxxMapper.xml文件里面、、、节点。

SqlSession

浅析Mybatis核心组件

  • SqlSession:它作为MyBatis提供的面向开发者的核心顶层API,表示和数据库交互时的会话对象,是MyBatis执行持久化操作的关键对象,它的底层封装了JDBC连接,用于完成数据库的增删改查功能。还有一点需要注意的是它不是线程安全的。
  • SqlSessionFactory:它是一个接口,它的定义就是创建SqlSession的工厂,Mybatis底层可以通过SqlSessionFactoryBuilder对象,加载xml配置文件或一个预设定的Configuration的实例构建出具体的工厂类的实例。
  • DefaultSqlSessionFactory:它是SqlSessionFactory默认实现类。它通过Configuration来创建,其重载了很多openSession方法(根据Configuration、执行器、自动提交创建等等)来创建SqlSession。
  • Configuration:主配置信息,详情见第一小节。
  • DefaultSqlSession:它是SqlSession接口的默认实现类,它不仅实现了SqlSession的核心方法,还维护主配置信息与执行器(Executor)信息。它的类注释上标注了此类不是一个线程安全的,为什么线程不安全?其实也很好理解,因为SqlSession的最终执行只会产生一次Connection,假设在两个线程通过同一个SqlSession来执行操作,那么就有可能,A线程开始执行,但是由于B线程执行操作比较快,B线程先关闭了链接,从而造成A线程的逻辑不被成功执行。
  • SqlSessionManager:可以看出SqlSessionManager既实现了SqlSessionFactory,也实现了SqlSession。所以它具备生产SqlSession的能力,也具备SqlSession的能力,SqlSession的作用是执行具体的Sql语句。但是需要说明的是,SqlSessionManager是线程安全的,因为它内部维护了一个sqlSessionProxy,其重写的SqlSession的方法,都是通过sqlSessionProxy来执行的,而sqlSessionProxy是一个动态代理的对象,其SqlSession是通过localSqlSession来获取的,这个属性其实就是一个ThreadLocal类,到这其实也就明白了,它为每一个线程分配一个副本对象,保证了Connection对象线程的安全性。
  • Executor:详情见下一小节。

Executor

浅析Mybatis核心组件

  • Executor:通过上面几个关系图,不难看出一个SqlSession对应一个Executor对象,而Executor是MyBatis的内部SQL执行器,它负责调用StatementHandler操作数据库进行增删改查的具体操作,并把结果集通过ResultSetHandler进行自动映射。
  • BaseExecutor:BaseExecutor是一个抽象类,它采用模板方法的方式实现了Exeutor接口,除了默认实现的方法之外,它提供了额外的抽象方法由不同功能的实现类实现不同的功能,其中它的实现类有SimpleExecutor、ReuseExecutor、BatchExecutor、ClosedExecutor,需要额外说明的是Mybatis的一级缓存的功能也是其实现的。
  • CachingExecutor:它其实是Executor的装饰器,它内部维护了一个Executor的属性,并为这个属性增加了二级缓存的功能。因为这个属性的类型是Executor,所以这个属性可以是BaseExeutor具体实现的执行器的任何一个,它的处理逻辑可以大致列为:它先从缓存中查询结果,如果存在则就返回。倘若不存在,则交由BaseExecutor的具体的实现类去进行操作,并把结果缓存起来。
  • SimpleExecutor:从它的名字也能看出它是一个简单的执行器,其核心的方法都在其父类BaseExecutor中,它与其他执行器的区别是,它每执行一次,就创建一个Statement对象,用完立刻关闭Statement对象。
  • BatchExecutor:专门用于执行批量sql操作。它维护了多个Statement,通过Statement.addBatch()添加到批处理,当所有参数都设置完,通过Statement.executeBatch()方法执行操作。
  • ClosedExecutor:它是一个内部类,外部无法使用,调用其方法会抛出异常。它是一个表示已经关闭的执行器,并没有实际的功能。
  • ReuseExecutor:它是可复用的执行器,它会重用Statement执行sql操作,在执行操作的时候,会以sql作为key去查找Statement,有就使用,没有就创建,使用完不会关闭,会暂存起来,以供下次使用。

StatementHandler

浅析Mybatis核心组件

  • StatementHandler:它是MyBatis操作数据库执行sql脚本的对象,也叫sql语法构建处理器,sql它封装了对JDBC的Statement对象的操作,其作用是:sql声明、填参、执行、获取结果。需要额外说明的是它的填参和获取结果的过程,因为它涉及参数映射、结果集映射、1对N、循环引用等复杂逻辑的处理。对于这种复杂性MyBatis做法就是使用参数处理器(ParameterHandler)、结果集处理器(ResultSetHandler)分别实现处理的。
  • BaseStatementHandler:它是StatementHandler的默认实现类,它也是一个抽象的基类,它有点类似于Executor,它同样采用模板方法的方式实现了StatementHandler接口,除了默认实现的方法之外,它提供了额外的抽象方法由不同功能的实现类实现不同的功能。
  • RoutingStatementHandler:它通过静态代理的模式,实现了“路由器”的功能,它主要功能是实现了此下三个处理器的路由,它没有实际操作处理,只是负责BaseStatementHandler相关具体的实现StatementHandler的创建及调用。
  • SimpleStatementHandler:它是一个简单的处理器,它对应着JDBC Statement对象的处理,处理不带动态参数运行的sql。
  • PrepareStatementHandler:它是能够解决sql注入的一种推荐处理器,它同样也对应java.sql.PrepareStatement对象的处理,负责处理sql中的占位符动态参数赋值操作预编译SQL的接口处理器。
  • CallableStatementHandler:它实际就是使用CallableStatement来执行 SQL 语句,只不过它执行的是存储过程。

ParameterHandler

浅析Mybatis核心组件

  • ParameterHandler:它是Mybatis的sql实参处理器,它主要负责将传递的参数转换成JDBC的Statement所需要的参数,它是MyBatis实现sql参数设置的接口对象。
  • DefaultParameterHandler:它是ParameterHandler的默认实现类,它主要是给PreparedStatement设置参数,将传入的BoundSql语句中的"?"占位符进行实参替换。MyBatis在解析xml文件中sql标签节点时,会将#{}参数解析为ParameterMapping对象,通过它就可以知道参数的名称、JavaType、JdbcType等等信息。再根据参数名称去ParamMap中解析出参数值,根据参数的JavaType找到对应的TypeHandler,再通过TypeHandler去设置对应类型的参数,

ResultSetHandler

  • ResultSetHandler:它封装了将JDBC返回的ResultSet结果集对象的操作。当执行SQL类型为SELECT语句时,ResultSetHandler用于将查询结果转换成Java对象。(ResultSet接口是JDBC-API中比较重要的组件,提供了检索和操作sql执行结果的方法)
  • DefaultResultSetHandler:它是ResultSetHandler的默认实现类,它具体的作用就是将Statement执行后的结果集,按照Mapper文件中定义返回结果的ResultType/ResultMap来封装成对应的对象,最后将封装的对象返回。

TypeHandler

浅析Mybatis核心组件

  • TypeHandler:它是MyBatis中的类型处理器,它负责java数据类型和JDBC数据类型之间的映射和转换,它的作用主要是能够根据java类型,在StatementHandler调用ParameterHandler或ResultSetHandler时,为对应的set/get方法为对象设值。
  • BaseTypeHandler:它是TypeHandler接口的默认实现类,它提供了4个抽象方法由具体的子类去实现相关的转换功能,而我们在实现自定义的转换器的时候,可以通过继承它实现相关功能。

总结

Sqlsession作为Mybaits开发者层面的顶层API,实际上是Executor组件的门面接口,其主要目的是为了提供更友好的数据库操作接口,这也是设计模式中外观模式的典型应用。真正执行sql操作的是Executor的sql执行器,它会通过StatementHandler组件对JDBC的Statement对象进行操作,它通过ParameterHandler组件对sql参数占位符赋值。ParameterHandler组件中会根据java类型找到对应的TypeHandler对象,TypeHandler中会通过Statement对象提供的set()方法为Statement对象中的参数占位符设置值。同样,当StatementHandler组件完成与数据库交互后,在通过ResultSetHandler组件从Statement对象中获取ResultSet对象,然后将ResultSet对象转换为对应的java对象。