MyBatis游玩

笔记学习自MyBatis技术内幕,作者的水平很高,虽然书里表面说的MyBatis源码,但实际上是谈论设计模式的具体应用,强推!

基础支持

类型转换

Java类型到Jdbc类型是怎么实现的?

MyBatis的类型转换器依赖于TypeHandler接口,其中getResult负责将Java类型转换成JdbcType类型,而setParameter则反之

内置的抽象类类为BaseTypeHandler,所有默认实现类都继承于它,基本是调用PreparedStatementResultSet方法来具体实现参数绑定/获取指定列值

TypeHandlerRegistry负责管理MyBatis初始化阶段创建的所有已知TypeHandler对象

具体的是提供映射(真·Map对象),核心字段有

JDBC_TYPE_HANDLER_MAP:用于表示JdbcTypeTypeHandler的唯一映射

TYPE_HANDLER_MAP:用于表示Type到多个JdbcTypeTypeHandler的映射(比如String会对应于char和varchar)

比如类型为Map<JdbcType,TypeHandler<?>>JDBC_TYPE_HANDLER_MAP字段(实质为EnumMap),当从结果集获取数据时,依靠这个还实现Jdbc类型到Java类型的转换

而关键的register的常见重载方法有(Type,JdbcType,TypeHandler)的入参,具体注册顺序为

1.检测javaType是否为空,无论是否为空均加入到ALL_TYPE_HANDLERS_MAP(key为handler.getClass())

2.如果非空,从TYPE_HANDLER_MAP中通过javaType获得对应的Handler的Map

3.如果没有对应Handler的Map,则通过直接新建一个空的HashMap并加入到TYPE_HANDLER_MAP

4.在对应的handler的Map加入(jdbcType,handler)键值对

这样实现了单个javaType对应多个jdbcType的handler映射注册,在此之后要获得typeHandler可通过getTypeHandler(type,jdbcType)得到,其实就是跑上面的Map的对应键值即可

类型别名的实现?

MyBatis通过TypeAliasRegistry完成表/列名的别名注册和管理

具体是通过Map<String,Class<?>> TYPE_ALIASES字段以及registerAlias()实现,就是个get检查有没有然后再put进去的过程(冲突会抛出异常!)。。

日志

MyBatis通过适配器模式实现对不同的日志接口/实现(Adaptee)的兼容,其内部使用接口为Log(适配器中的Target),由LogFactory来实现适配过程(Adapter)

资源加载

MyBatis封装了ClassLoaderWrapper作为ClassLoader的包装器,使用起来和ClassLoader一致,但可调整ClassLoader使用顺序(多个ClassLoader依次检查,并从中挑出可用的)

该类中有两个字段defaultClassLoadersystemClassLoader,前者由系统指定,后者由ClassLoader.getSystemClassLoader()提供

该ClassLoaderWrapper中的getClassLoaders(classLoader)返回ClassLoader[]数组,分别是

classLoader / defaultClassLoader / Thread.currentThread().getContextClassLoader() / getClass().getClassLoader() / systemClassLoader

不同的类加载器具有不同的访问权限,对于获取资源如URL getResourceAsURL(resource,classLoader[])就会依据上面给出的类加载器顺序依次获取资源(失败后会再通过"/"+resource再次尝试)

DataSource

数据源组件需要通过实现javax.sql.DataSource接口来实现,MyBatis通过经典的工厂模式来提供两个实现类PooledDataSourceUnpooledDataSource,其对应的工厂类为实现DataSourceFactoryPooledDataSourceFactoryUnpooledDataSourceFactory,其工厂接口(关注创建API)-工厂实现(关注创建逻辑)-产品接口(关注用户API)-产品实现(关注业务逻辑)的设计典范是十分规范的面向接口编程

在实现类中均封装好的注册JDBC驱动、建立Connection等模板

PooledDataSource不直接管理javax.sql.Connection对象,而是委托给PooledConnection,它直接封装了Connection对象及其代理(比方说当调用代理的close()时实际是调用PooledDataSource.pushConnection()

并且由PooledState通过ArrayList<PooledConnection>管理idle的连接

事务管理

似乎没啥好说的?也是个工厂实现

缓存

Cache接口提供非常多的缓存实现装饰器,比如SoftCacheLruCacheSynchronizedCache

Cache中指定的key CacheKey不是一个简单的String,内部封装了multiplier/hashCode/checkSum/count等用于重写hashCode()equals()的字段,还有List<Object> updateList会记录多种影响缓存项的因素(既可通过多个对象组成一个key),比如MappedStatement的id/查询结果集的范围/查询使用的SQL/传递到SQL的参数,每次update(obj)到一个key时会判断是否是数组,如果是则添加多个内部对象到cacheKey,不管如何都是通过doUpdate(obj)来具体更新,四个字段更新:count++,checkSum+=baseHashcode,baseHashCode*=count,hashCode=multiplier*hashCode+baseHashCode,最后把obj放入到updateList中,判断key是否相等则依次按照上述字段进行对比,迫不得已才用updateList的元素比较

Binding

Binding实现xml/注解到接口的绑定(就是一堆Mapper就能实现执行SQL的原理)

从而实现SQL到Java层的对接

XxxMapper mapper = session.getMapper(XxxMapper.class);

Xxx obj = mapper.do(...);

POJO到mapper的实现?

就是个面向接口的动态代理,因此写mapper时声明interface

解析器

解析器指对XML的处理,MyBatis使用DOM解析方式,具体使用到XPath解析并将其封装到XPathParser

反射模块

MyBatis提供封装好的reflection

核心处理

配置解析

MyBatis初始化工作为加载并解析mybatis-config.xml配置文件、映射配置文件和相关的注解

其初始化入口为SqlSessionFactoryBuilder.build(reader,enviroonment,properties)

具体的会先尝试创建XMLConfigBuilder parser,如果成功则委托给build(parser.parse())

XMLConfigBuilder就是一个BaseBuilder抽象类的具体实现

BaseBuilder包含一个全局唯一配置configuration,别名标记typeAliasRegistry,handler注册中心typeHandlerRegistry,后面两个在前面已经讨论过细节

外部API流程

1.通过配置文件生成SqlSessionFactory对象,SqlSessionFactory生成SqlSession(openSession())

2.SqlSession接口定义执行SQL所需的方法

3.通过SqlSession对象执行配置中的SQL语句

4.通过SqlSession对象提交事务

5.关闭SqlSession

发表评论

电子邮件地址不会被公开。 必填项已用*标注