Mybatis

这个学习资料来源就忘了,好像还是B站老杜?

简介

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

官方中文文档:mybatis – MyBatis 3 | 入门

是一款持久层框架

Mybatis的底层:使用动态代理

Mybatis的本质:使用动反射机制

持久化

持久化就是将程序的数据在持久状态和瞬时状态转化的过程

  • 内存:断电即失
  • 数据库(jdbc):io文件持久化

为什么需要持久化

  • 部分对象不可丢失
  • 内存比存储贵

持久层

Dao层、Service层、Controller层

  • 完成持久化工作的代码块
  • 层界限十分明显

特点

  • 方便
  • JDBC代码复杂、需要简化、框架、自动化
  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射。
  • 提供对象关系映射标签,支持对象关系组建维护。
  • 提供xml标签,支持编写动态sql。

第一个程序

搭建环境 -> 导入Mybatis -> 编写代码 -> 测试

流程:

  1. 首先创建简单的sql maven项目
  2. 依赖引入,包括mysql、mybatis、junit
  3. 创建实体类:Dept:实体类须包含数据库内各个字段且名称能一一对应
  4. 创建接口dao:DeptMapper 主要用于方法映射,第一个方法为 List getAllDept();
  5. 创建工具函数MybatisUtils:主要用于取出sqlSession对象和关闭该对象,其中需要引入mybatis-config.xml文件
  6. 创建mybatis-config.xml文件,该文件用于数据库连接和数据库配置,其中需要引入DeptMapper.xml文件,用于数据库的增删改查语句执行
    • 该文件包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)
    • name=”driver”
    • name=”url”
    • name=”username”
    • name=”password”
    • 用作连接数据库相关配置
    • 用于映射数据库操作文件
  7. 创建DeptMapper.xml文件,要引入对应的接口文件作为命名空间:namespace=”org.example.dao.DeptMapper”
  8. 在DeptMapper.xml书写sql语句,绑定的id为DeptMapper接口对应方法,resultType为返回类型,一般是实体类,parameterType为查询对象
  9. 在test中编写将sqlsession创建并执行,遇到增删改的相关操作,需要提交事务(commit)

配置解析

核心配置文件

环境配置(environment)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--todo 环境变量-->
<!--default可以用于环境切换-->
<environments default="development">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据库连接池-->
<dataSource type="POOLED">
<!--相关属性配置-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>

<!--家里的相关配置 <environment id="home"> </environment>-->
</environments>

属性(properties)

1
2
3
4
5
<!--todo 读取属性文件(jdbc.properties)-->
<properties resource="jdbc.properties">
<!--todo 优先从文件配置读取,文件配置没有再从以下读取-->
<!--<property name="password" value="123456"/>-->
</properties>
1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username=root
password=123456

类型别名(typeAliases)

1
2
3
4
<!--todo 类型别名-->
<typeAliases>
<typeAlias type="org.example.pojo.Dept" alias="dept"></typeAlias>
</typeAliases>
1
2
3
4
5
<!--todo 包别名-->
<!-- 引用的包的类默认小写-驼峰命名-->
<typeAliases>
<package name="org.example.pojo"/>
</typeAliases>

设置(settings)

image-20220814125909578

image-20220814125844813

image-20220814125857443

其他配置

plugins插件:

  1. mybatis plus
  2. 通用mapper

映射器(mappers)

  1. 使用相对于类路径的资源引用

    1
    2
    3
    4
    5
    6
    <!-- 使用相对于类路径的资源引用 -->
    <mappers>
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
    <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
    <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
  2. 使用完全限定资源定位符(URL) – 不建议使用

    1
    2
    3
    4
    5
    6
    <!-- 使用完全限定资源定位符(URL) -->
    <mappers>
    <mapper url="file:///var/mappers/AuthorMapper.xml"/>
    <mapper url="file:///var/mappers/BlogMapper.xml"/>
    <mapper url="file:///var/mappers/PostMapper.xml"/>
    </mappers>
  3. 使用映射器接口实现类的完全限定类名

    1
    2
    3
    4
    5
    <mappers>
    <mapper class="org.mybatis.builder.AuthorMapper"/>
    <mapper class="org.mybatis.builder.BlogMapper"/>
    <mapper class="org.mybatis.builder.PostMapper"/>
    </mappers>
  4. 将包内的映射器接口实现全部注册为映射器

    1
    2
    3
    <mappers>
    <package name="org.mybatis.builder"/>
    </mappers>

注意:在使用方法三和四时,须遵循以下规则,否则报错

  • 接口和该mapper配置文件必须同名
  • 接口和该mapper配置文件必须处于同一包下

建议:不论采用哪种方式,都遵循上面配置点(规范来着)

生命周期和作用域

mybatis – MyBatis 3 | 入门- 生命周期

Mybatis生命周期和作用域_小小代码熊的博客-CSDN博客_mybatis生命周期和作用域

SqlSessionFactoryBuilder

Mybatis执行流程

Mybatis执行流程_知知之之的博客-CSDN博客_mybatis执行流程

MyBatis 的执行流程_azhou的代码园的博客-CSDN博客_mybatis执行流程

日志工厂

指定 MyBatis 所用日志的具体实现,未指定时将自动查找

类型

  • SLF4J
  • LOG4J(3.5.9 起废弃)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING(常用)
  • NO_LOGGING
1
2
3
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

log4j

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件

通过log4j,我们可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

实践略过,已废弃

分页

mybatis分页

xml:

1
2
3
4
5
<!--todo 分页查询-->
<select id="getAllByPage" resultMap="DeptMap" parameterType="map">
select *
from Dept limit #{pageStart},#{length};
</select>

test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testPage() {
SqlSession sqlSession = MybatisUtils.getSqlSession();

HashMap<String,Object> map = new HashMap<>();
map.put("pageStart",1);
map.put("length",3);
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> depts = mapper.getAllByPage(map);
for (Dept dept : depts) {
System.out.println(dept);
}

MybatisUtils.closeSqlSession(sqlSession);
}

其他相关插件

  1. RowBounds分页
  2. MyBatis 分页插件 PageHelper

注解开发

使用注解开发,免去xml配置的麻烦,但

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

选择何种方式来配置映射,以及是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松地在基于注解和 XML 的语句映射方式间自由移植和切换。

第一步:配置config.xml

1
2
3
<mappers>
<mapper class="org.example.dao.DeptMapper"></mapper>
</mappers>

第二步:编写DeptMapper接口的注解查询

1
2
3
// todo 查询全部部门
@Select("select * from dept")
List<Dept> getAllDept();

第三步:测试,同之前一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testA() {
SqlSession session = null;
try {
session = MybatisUtils.getSqlSession();
DeptMapper deptMapper = session.getMapper(DeptMapper.class);
List<Dept> allDept = deptMapper.getAllDept();
for (Dept dept : allDept) {
System.out.println(dept);
}
} finally {
MybatisUtils.closeSqlSession(session);
}
}
  1. 1
    2
    3
    //todo 增加部门
    @Insert("insert into dept (deptno, dname, loc) values(#{deptno},#{name},#{loc})")
    int insertOne(@Param("deptno") int deptno, @Param("name") String name, @Param("loc") String loc);
  2. 1
    2
    3
    4
    //todo 删除部门
    // 当只有一个参数时,可以省略不写,但是建议还是写上注解
    @Delete("delete from dept where deptno = #{deptno}")
    int deleteOne(int deptno);
  3. 1
    2
    3
    //todo 修改部门
    @Update("update dept set dname = #{name}, loc = #{loc} where deptno = #{deptno}")
    int updateOne(@Param("deptno") int deptno, @Param("name") String name, @Param("loc") String loc);

    param

  • 基本类型的参数或者String类型需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型可以忽略(但不建议)
  • 在SQL引用的就是param()中设定的属性名

LomBok

Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,你的类就有一个功能齐全的构建器、自动记录变量等等

  1. idea引入插件
  2. maven引入依赖

多对一查询

-> Mybatis07

一对多查询

-> Mybatis08

动态SQL

动态SQL是根据不同条件生成不同SQL语句

-> Mybatis09

其他直接看官方文档

trim

1
2
3
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

缓存

-> Mybatis10

  1. 什么是缓存[ Cache ]
    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据。[可以使用缓存]

学习名词:主从复制、读写分离、高并发

Mybatis缓存

MyBatis内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

1
<cache/>

一级缓存

测试注意点

  1. 开启日志
  2. 在一个session中查询两次
  3. 查看日志输出

缓存失效情况

  1. 查询不同东西
  2. 增删改操作,以为会涉及到数据的改变
  3. 查询不同的mapper.xml
  4. 手动清理缓存

一级缓存是默认开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段,一级缓存相当于一个Map

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存(map) 中
    • 对应的实体类要序列化,否则会报错

总结:

  1. 只要开启了二级缓存,在同一个Mapper下就有效
  2. 所有的数据都会存放在一级缓存中
  3. 只有当会话提交或者关闭的时候,才会提交到二级缓存中

缓存原理

还需完善

未命名文件

自定义缓存-ehcache

ehcache

Ehcache 入门详解 - 王旌羽 - 博客园 (cnblogs.com)

面试题

MyBatis面试题(总结最全面的面试题) - 掘金 (juejin.cn)

  1. MySQL引擎
  2. InnoDB底层原理
  3. 索引
  4. 索引优化

Mybatis执行流程剖析

mybatis源码学习