不与夏虫语冰,不与井蛙语海,不与凡夫语道
1 简介
1.1 定义
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.1.1 背景
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github
1.1.2 为什么使用
- 框架,自动化
- 方便存入数据库
- 技术没有高低之分
- 用的人多,有生态
- 优点
简单易学、灵活、解除sql与程序代码的耦合、提供映射标签、提供对象关系映射标签、提供xml标签
1.1.3 获取方式
1 | <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> |
1.2 持久化
数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称. 数据模型可以是任何数据结构或对象模型,存储模型可以是关系模型、XML、二进制流等。cmp和Hibernate只是对象模型到关系模型之间转换的不同实现。
1.2.1 优点
- 程序代码重用性强
- 业务逻辑代码可读性强
- 持久化技术可以自动优化,数据持久化对象的基本操作有:保存、更新、删除、查询等。
1.3 持久层
数据持久层位于领域层和基础架构层之间。由于对象范例和关系范例这两大领域之间存在“阻抗不匹配”,所以把数据持久层单独作为J2EE体系的一个层提出来的原因就是能够在对象-关系数据库之间提供一个成功的企业级映射解决方案,尽最大可能弥补这两种范例之间的差异。
- 数据持久化的代码块
- 通用的持久化
2 第一个Mybatis
搭建环境-导入包-编写代码-测试
2.1 创建一个数据库的表
1 | create database xhr; |
2.2 新建项目
2.2.1 步骤
- 普通的Maven项目
- 删除src
- 导入maven依赖
1 |
|
- 创建一个模块
- 编写mybatis配置文件
1 |
|
- 创建entity
1 | package cn.xxy.entity; |
- 创建mapper
1 | package cn.xxy.mapper; |
1 |
|
- 测试
1 | package cn.xxy.mappertest; |
可能出现的问题
资源访问不到
1 | <build> |
- mapper配置文件初始化失败
1 | 看返回类型 |
3 CRUD
3.1 namespace【命名空间】
命名空间要与mapper包名的接口名字一样
3.2 增删改
需要提交事务
- mapper
1 | int insertUser(int age); String id, String name, |
- mapper.xml
1 | <insert id="insertUser" parameterType="cn.xxy.entity.User"> |
- 测试
1 |
|
- 可能出现问题
- 数据库列的编码与类编码不同
修改数据库列的编码utfmb4,不要使用utf8这个是没用的
- 提交事务
1 | //提交事务 |
3.3 传递参数
- 只有一个基本类型,可以直接在sql取到
- 对象传递参数
- map传递【实际开发除了特殊情况,其他时候不推荐】
1 | parameterType="map" |
3.4 模糊查询
- mapper
1 | List<User> selectUserByName(String name); |
- xml
1 | <select id="selectUserByName" parameterType="String" resultType="cn.xxy.entity.User"> |
- 测试
1 |
|
3.5 属性名和字段名不一致问题
- 方式一:数据库操作语句起别名 as
1 | <select id="selectUserByName" parameterType="String" resultType="cn.xxy.entity.User"> |
- 方式二:resultType用的是简称类名,需要结果集映射
1 | <resultMap id="BaseResultMap" type="cn.xxy.entity.User" > |
4. 配置
4.1 配置文件顶层结构
1 | configuration(配置) |
4.2 环境变量【environments】
MyBatis 可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
1 |
|
4.3 属性【properties】
引用外部文件,其中可以增加属性配置。相同优先使用外部配置文件
4.4 别名【typeAliases】
- 自定义
1 | <typeAliases> |
- 扫描包,如何要扫描又要自定义可以使用注解
1 | <typeAliases> |
1 |
|
4.5 设置
1 | <settings> |
4.6 映射器【mappers】
- 方式一:使用相对于类路径的资源引用 【推荐】
1 | <mappers> |
- 方式二:使用完全限定资源定位符(URL)【不建议】
1 | <mappers> |
- 方式三:使用映射器接口实现类的完全限定类名
接口和他的Mapper配置文件必须同名
接口和他的Mapper配置文件必须在同一个包下
1 | <mappers> |
- 方式四:将包内的映射器接口实现全部注册为映射器【包扫描】
接口和他的Mapper配置文件必须同名
接口和他的Mapper配置文件必须在同一个包下
1 | <mappers> |
4.7 其他配置
类型处理器(typeHandlers)
objectFactory(对象工厂)
plugins(插件)
- MyBatis Plus
- MyBatis Generator Core
4.8 生命周期
生命周期和作用域错误使用会导致严重的并发问题
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了【局部变量】
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在【相当于数据库连接池,全局变量】,因此 SqlSessionFactory 的最佳作用域是应用作用域。最简单使用单例模式或者静态单例模式
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
4.9 结果映射
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
1 | <!-- 非常复杂的语句 --> |
1 | <!-- 非常复杂的结果映射 --> |
constructor
- 用于在实例化类时,注入结果到构造方法中
idArg
- ID 参数;标记出作为 ID 的结果可以帮助提高整体性能arg
- 将被注入到构造方法的一个普通结果
id
– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能result
– 注入到字段或 JavaBean 属性的普通结果association
– 一个复杂类型的关联;许多结果将包装成这种类型
- 嵌套结果映射 – 关联可以是
resultMap
元素,或是对其它结果映射的引用
- 嵌套结果映射 – 关联可以是
collection
– 一个复杂类型的集合
- 嵌套结果映射 – 集合可以是
resultMap
元素,或是对其它结果映射的引用
- 嵌套结果映射 – 集合可以是
discriminator
– 使用结果值来决定使用哪个
resultMap
case
– 基于某些值的结果映射
- 嵌套结果映射 –
case
也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
- 嵌套结果映射 –
5. 日志
5.1 日志工厂
出错,日志是最好的助手
以前:sout、debug
日志工厂
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
5.2 日志设置
1 | <settings> |
5.3 log4j【有漏洞了解使用方式即可】
可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- pom.xml
1 | <!-- https://mvnrepository.com/artifact/log4j/log4j --> |
- log4j.properties
1 | log4j.rootLogger=INFO,Console,File |
6. 分页
减少数据库查询压力
使用limit,默认为0,-1这个数字bug已经修复
使用rowbounds,开发中不建议使用
使用插件mybatis-helper
使用 MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
- 使用拦截器
7. 注解
底层主要是动态代理,本质是反射
7.1 面向接口开发【解耦】
面向对象,以对象为单位,考虑它的属性和方法
面向过程,考虑具体的流程为一个单位
面向接口和上面两种不是一个问题,更体现为系统整体的架构
7.2 测试
- 接口
1 |
|
- 绑定接口【很重要】
1 | <mappers> |
- 测试
1 |
|
7.3 @Param
- 有多个参数需要加上,一个建议加上,@Param(“age”)SQL引用的就是注解括号里的名字
1 | int insertUser(int age); String id, String name, |
7.4 Lombok
适合就行
- Idea库安装插件
- 导入依赖
1 | <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> |
- 加注解
1 | @Data |
8. 映射
8.1 测试
8.1.1 新建实体类与数据库一致
8.1.2 建立Mapper接口和映射文件.xml文件
8.1.3 在核心配置文件绑定注册Mapper接口或文件【方式很多】
1 | <mapper resource="cn/xxy/mapper/UserMapper.xml" /> |
8.1.4 测试查询是否成功
8.2 多对一映射
8.2.1 按照查询嵌套查询【核心代码像子查询】
1 | <resultMap id="BaseResultMap" type="cn.xxy.entity.Student" > |
8.2.2 按照查询嵌套查询【核心代码 联表查询】
1 | <select id="selectStudentAndTeacher2" resultMap="StudentAndTeacher2"> |
8.3 一对多映射
8.3.1 按照结果嵌套处理
1 | <!--按照结果嵌套查询--> |
8.3.2 按照查询嵌套处理
1 | <select id="selectTeachers" resultMap="TeacherStudent2"> |
9. 动态SQL
动态SQL就是能够根据不同的条件生成不同的SQL语句,本质还是SQL语句
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
9.1 if【会把if全部执行】
1 | <select id="selectStudentByIf" parameterType="cn.xxy.entity.Student" resultMap="BaseResultMap"> |
- 测试
1 |
|
9.2 choose (when, otherwise)【会到第一条真的语句,后面不再执行】
1 | <select id="selectStudentByChoose" parameterType="cn.xxy.entity.Student" resultMap="BaseResultMap"> |
9.3 trim (where, set)【去前缀例如逗号,and,or这些】
使用 trim标签可以完成where标签相同的功能,
1 | <trim prefix="WHERE" prefixOverrides="AND"> |
9.4 foreach
1 | <select id="selectPostIn" resultType="domain.blog.Post"> |
9.5 SQL片段【相同的SQL语句】
1 | <sql id="selectif"> |
- 注意:
- 最好基于单表
- SQL片段不要存在where标签
10. 缓存
解决高并发系统的性能
10.1 一级缓存
也叫本地缓存:SqlSession,默认开启,只在一次SqlSession中有效
10.2 缓存失效的情况
- 查询不同的数据
- 增删改会改变原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
10.3 二级缓存
也叫全局缓存,一级缓存作用域太低,一个名称空间【namespace】对应一个二级缓存,二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
- 开启全局缓存
1 | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 |
- 在想要二级缓存的Mapper.xml中开启
1 | <cache/> |
或
1 | <cache |
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
- 小结
- 实体类需要序列化,否则会抛出没有序列化异常
- 只要开启二级缓存,在同一个Mapper下就有效
- 所有的数据都先放在一级缓存中
- 只有当前会话提交或者关闭,才会提交到二级缓存中
- 用户进来系统先查二级缓存,然后一级缓存,最后数据库
10.4 自定义缓存
Ehcache是一种广泛使用的开源Java分布式缓存。一般是使用Redis
11. 总结
对于Mybatis进行了系统的学习,接下来会对源码进行学习