深入浅出MyBatis
第一章 MyBatis简介
1.3 Hibernate
Hibernate的缺点(按致命程度排序):
- HQL性能较差,开发人员不能对SQL进行优化。
- Hibernate是全表映射,导致每次更新都要发送全部字段的数据,每次查询也是查询所有字段的数据,非常麻烦,也非常影响效率;
- 没有动态的SQL查询条件,也不能使用动态的表名,导致数据库不能根据业务分表,非常不灵活;
- 对多表查询的支持很差,不支持存储过程。
1.4 Mybatis
Hibernate太智能,把很多事都做了,为开发人员节省了很多事, 但是物极必反,开发人员做得太少就一定要牺牲某一方面的优点, 这里很明显是牺牲了性能。
在计算机领域这样的例子非常多,最终我们会选择一个折中的办法来解决这样一个问题, 而Mybatis就是这样一个折中的办法。 如果说Hibernate是全自动ORM框架的话,Mybatis就是一个半自动的ORM框架。
第二章 Mybatis入门
2.2 Mybatis的基本构成
- SqlSessionFactoryBuilder(构造器):根据配置信息或者代码生成SqlSessionFactory(工厂接口)。这里采用建造者模式,对于用户来说,只需要关注自己的数据库相关信息即可,具体MyBatis是如何根据数据的相关信息来操作数据库的,对用户来说是透明的;
- SqlSessionFactory:接口定义了对数据库的操作,具体实现类是SqlSessionFactoryBuilder根据用户的数据库配置来确定的,可以依靠该工厂接口来生成对数据库的连接SqlSession(会话);
- SqlSession:执行SQL;
- SQL Mapper:由Java接口和xml文件组成。
2.2.1 构建SqlSessionFactory
MyBatis读取配置文件信息,将信息存储到org.apache.ibatis.session.Configuration的对象中,Configuration对象存在于整个Mybatis的生命周期,SqlSessionFactoryBuilder读取Configuration对象来创建SqlSessionFactory。 SqlSessionFactory有两个Mybatis自带的实现类,DefaultSqlSessionFactory和SqlSessionManager,不过SqlSessionManager目前还没有被使用,默认使用DefaultSqlSessionFactory。
2.2.2 创建SqlSession
通过SqlsessionFactory的openSession()创建SqlSession,SqlSession的实现类有DefaultSqlSession和SqlSessionManager。 Sqlsession有两种用法:
- 通过getMapper()获取映射器,然后使用映射器去执行sql;
- 直接通过sql的命名空间加id执行sql,这种方法是在iBatis中保留下来的,将sqlid写到Java代码里面去,这种方法是非常不好,既能利用Java的编译器校验是否拼写错误,也不利于项目后期的维护。不提倡,所以我不会这么写。
2.2.3 映射器
从用户的角度来看,映射器由Java接口和XML配置文件组成,映射的组成部分在6.2.2节。
2.3 生命周期
2.3.1 SqlSessionFactoryBuilder
用于构建SqlSessionFactory,而SqlSessionFactory是单例的,所以构建完成之后就不需要了建造者了,可以回收了。
2.3.2 SqlSessionFactory
用于在每次访问数据库的时候创建SqlSession,相当于打开一个数据库会话(一般是从数据库连接池里面拿一个已经存在的会话) 所以SqlSessionFactory应该存在于Mybatis的整个生命中周期中, 而一个SqlSessionFactory对象对应一个数据库,并且SqlSessionFactory对象是单例的。
2.3.3 SqlSession
用于一次业务中的所有次访问数据库,所以它的生命周期是在一次业务请求中,也可以说是在请求数据库处理事务的过程中。 是一个线程不安全的对象。(这里的线程不安全,我认为是说多个不同的SqlSession同时访问一个数据库的时候,并不是多个线程共用一个SqlSession, 不过具体还是要看连接池的实现,如果获取SqlSession的时候没有做同步处理,多个线程共用一个SqlSession也是有可能的。)
2.3.4 Mapper
用于执行SQL,生命小于等于SqlSession,是一个方法级别的东西。
第三章 配置
Mybatis配置文件xml的层次结构是有顺序的,不能随意颠倒顺序,否则解析的时候会出现异常!!!
三种配置方式:
- Mybatis配置文件中的property子元素,可以配置数据库相关信息
- Properties配置文件方式,一般用于配置jdbc相关信息
- 程序参数传递,直接在代码中写死参数
优先级:property子元素<properties配置文件<程序参数传递
不要使用混合方式,首选properties文件的方式。
3.3 别名
Mybatis里面有很多自定义的别名,可以在org.apache.ibatis.type.TypeAliasRegistry中查看,所以我们在写sql的时候只有一个参数,我们可以用string来表示java.lang.String等。
3.4 typeHandler类型处理器
用于处理数据库字段类型和Java数据类型之间的转换,一般用Mybatis自带的就可以了,也可以自定义。
3.5 ObjectFactory
当查询完返回结果的时候,使用的是ObjectFactory去构建POJO,他的默认实现类是org.apache.ibatis.reflection.factory.DefaultObjectFactory,也可以自定义,但是一般用默认的就可以了。
自定义的话可以实现ObjectFactory接口,也可以继承DefaultObjectFactory,同时在配置文件中配置
3.7 environments配置环境
3.7.3 数据源
三种数据源的实现方式:
- UNPOOLED,非连接池。org.apache.ibatis.datasource.unpooled.UnpooledDataSource。
- POOLED,连接池。org.apache.ibatis.datasource.pooled.PooledData.Source
- JNDI。org.apache.ibatis.datasource.jndi.JndiDataSourceFacotry
第四章 映射器
4.2 select元素
4.2.3 自动映射
在
- NONE,不自动映射;
- PARTITAL,自动映射,但没有嵌套映射,常用的是这种,默认的也是这种;
- FULL,全部映射,包括嵌套映射,性能低,一般不使用。 (这里也有之前说到的物极必反的意思,过于自动化了,开发人员不好控制了,而且也牺牲了一定的性能,完全不映射也不行,所以选择PARTITAL)
4.3 insert元素
4.3.2 主键回填和自定义
需求:如果表t_role没有记录,则我们需要设置id=1,否则我们就取最大id加2,来设置新的主键。
<insert id="insertRole" parameterType="role" useGeneratedKeys="true" keyProperty="id">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
Select if(max(id) is null, 1 , max(id) + 2) as new id from t_role
</selectKey>
Insert into t_role(id, role_name, note) values (#{id}, #{roleName}, #{note})
</insert>
其中order=”BEFORE”是说在insert语句之后把主键放回到传入对象中。
4.6 sql元素
个人认为该标签的使用大大降低了sql的可读性,并大大增加了sql的维护成本,个人不喜欢用。
还有级联,个人认为是一个很复杂,又很浪费性能,也不能带来什么好处的功能,个人不喜欢用。
4.8 缓存
4.8.1 系统缓存(一级缓存和二级缓存)
默认情况下,Mybatis不开启缓存。
一级缓存只对SqlSession有效,若其他SqlSession修改了数据,缓存并不会被修改,就会读取到脏数据。
二级缓存在一个namaspace中缓存,如果不同的namespace涉及到相同的表,也会读取到脏数据。
所以与其使用Mybatis的缓存机制,不如直接使用Redis等做集中式缓存,将Mybatis仅当做一个单纯的ORM框架来使用就好。
缓存相关连接:https://tech.meituan.com/mybatis_cache.html
第五章 动态SQL
5.7 bind元素
不同的数据库连接符不同,我们可以使用bind元素减少一些麻烦。
<select id="findRole" resultType="com.learn.chapter5.mybatis.bean.RoleBean">
<bind name="pattern" value="'%' + _parameter + '%'"/>
select id, role_name as roleName, create_date as createDate, end_date as endDate, end_flag as endFlag, note
from t_role
where role_name like #{pattern}
</select>
_paramter参数是怎么回事
第六章 Mybatis的解析和运行原理
6.2 构建SqlSessionFactory
第一步:通过org.apache.ibatis.builder.xml.XMLConfigBuilder解析xml配置文件, 将配置信息存入org.apache.ibatis.session.Configuration中。
第二步:使用Configuration构建SqlSessionFactory。
6.2.1 构建Configuration
6.2.2 映射器的内部组成
在2.2.3节中讲过,映射器由Java接口和xml文件组成,xml文件就是指我们写SQL的那个文件。
-
MappedStatement,相当于xml文件中的一个节点(select insert delete update)。 - SqlSource,是一个接口,主要作用是根据参数和规则组装SQL,我们在xml里面写的动态SQL就是由他来组装,组装好之后存到BoundSql里面的SQL参数里面; 他是提供BoundSql对象的地方,是MappedStatement的一个属性。
- BoundSql,保存SQL最重要信息的位置,有三个重要参数:
- SQL,也就是我们在xml中写的SQL最终执行时的样子,也就是日志里面打印出来的带有?的SQL;
- parameterObject,参数对象, Mybatis会将对象转换成Map<String,Object>对象,属性名为String类型的key,属性值为Object类型的value。 当我们使用@Param注解传入参数时,xml里面不需要写parameterObject参数, 但是Mybatis还是会将注解传入的参数转换成Map<String,Object>对象,key和value由注解指定;
- parameterMappings,是一个由ParameterMapping对象组成的List,存储数据库每一个字段对应Java的Mapping信息。
6.2.3 构建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); Mybatis根据Configuration的配置信息构建SqlSessionFactory对象。
6.3 SqlSession运行过程
6.3.2 SqlSession下的四大对象
Mapper执行的过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库操作和结果返回的。
- Executor,执行器,由他来调度StatementHandler、ParameterHandler、ResultHandler来执行SQL。在配置文件中的setting元素的defaultExecutorType属性配置,有以下三种,不同的执行器对应不同的StatementHandler:
- SIMPLE,默认的执行器
- REUSE,可以重用预处理语句的执行器
- BATCH,重用预处理语句,并批量更新,针对批量专用的执行器
- StatementHandler,数据库会话器,使用数据库的Statement(PrepareStatement)执行操作,是核心,
对应于上面三种Executor,这里也有三种;
- SimpleStatementHandler
- PreparedStatementHandler
- CallableStatementHandle
- ParameterHandler,处理SQL的参数;
- ResultHandler,封装执行结果并返回。
6.3.2.2 数据库会话器
Executor会先调用StatementHandler的prepare()方法预编译SQL语句, 然后调用StatementHandler的parameterize()方法启用ParameterHandler设置传入的参数,预编译就完成了。 接下来就执行SQL语句,执行完之后用ResultHandler处理结果类型,然后用ObjectFactory提供的规则组成成对象返回给用户。
6.3.2.3 参数处理器
用于设置参数,从parameterObject对象中取出参数,然后用相应的typeHandler对象进行参数处理,而这两个对象的信息都存储在Configuration中,可以直接拿来用。
6.3.2.4 结果处理器
handlerResultSets()方法用于包装结果集,默认的实现类是DefaultResultSetHandler。