Mybatis框架

Java框架(Framework)

框架技术

1
2
3
是一个应用程序的半成品
提供可重用的公共结构
按一定规则组织的一组组件

优势

1
2
3
4
5
不用再考虑公共问题

专心在业务实现上

结构统一,易于学习、维护

主流框架介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Struts2

MVC设计模式的重现

拦截器

可变和可重用的标签

HIBERNATE

ORM,简化数据库操作

Dao层

Spring
依赖注入容器/AOP实现

声明式事务

简化Java EE应用

粘合剂,将大家组装到一起

Spring MVC

结构最清晰的MVC Model2实现

高度可配置,支持多种视图技术

定制化开发

MyBatis

半自动化的ORM实现

DAO层

动态SQL

持久化与ORM(Object Relational Mapping)

1
2
3
4
5
6
7
8
9
持久化是程序数据在瞬时状态和持久状态转换的过程
ORM
编写程序的时候,以面向对象的方式处理数据
保存数据的时候,却以关系型数据库的方式存储
ORM解决方案包含下面四个部分
在持久化对象上执行基本的增删改查操作
对持久化对象提供一种查询语言或者API
对象关系映射工具
提供与事务对象交互、执行检查、延迟加载以及其他优化功能

MyBatis image-20211108200011135

1
2
3
4
5
6
7
8
9
10
11
MyBatis前身是iBatis,本是Apache的一个开源项目
官网
http://mybatis.org
ORM框架
实体类和SQL语句之间建立映射关系
特点
基于SQL语法,简单易学
能了解底层封装过程
SQL语句封装在配置文件中,便于统一管理与维护,降低程序的耦合度
方便程序代码调试
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

一、安装MyBatis

pom.xml依赖

1
2
3
4
5
6
7
8
9
10
11
<!--版本号-->
<properties>
<mabits-version>3.2.2</mabits-version>
</properties>

<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mabits-version}</version>
</dependency>

二、使用MyBatis的开发步骤

1
2
3
4
5
6
7
8
下载mybatis-3.2.2jar包并导入工程
编写MyBatis核心配置文件(configuration.xml)
创建实体类-POJO
DAO层-SQL映射文件(mapper.xml)
创建测试类
读取全局配置文件mybatis-config.xml
创建SqlSessionFactory对象,读取配置文件
创建SqlSession对象

database.properties

1
2
3
4
5
6
7
jdbc.driver=com.mysql.jdbc.Driver
#数据库
jdbc.url=jdbc:mysql://localhost:3306/leavedb?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
#用户名
jdbc.username=root
#密码
jdbc.password=123456

mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入外部文件-->
<properties resource="database.properties"></properties>
<!--别名-->
<typeAliases>
<package name="cn.cyg.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>

从 XML 中构建 SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取sqlSessionFactory对象
String resource = "mybatisconfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
public static SqlSession getSqlSession() {
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
//关闭SqlSession
public static void closeSqlSession(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.close();
}
}
}

从 SqlSessionFactory 中获取 SqlSession

既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

1
2
3
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换

1
2
3
4
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}

三、编写代码

实体类–>pojo

1
2
3
4
5
6
7
8
9
public class Student {
private Integer studentNo;
private String loginPwd;
private String studentName;
private String sex;
private Integer gradeId;
private String phone;
private String gName;
}

接口–>mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface StudentMapper {
//查询所有学生
List<Student> getAllStudent();
//根据学号studentNo查询学生
Student getStudentByStudentNo(Integer studentNo);
//根据姓名studentName模糊查询
List<Student> getStudentByStudentName(@Param("studentName") String studentName, @Param("gradeId")Integer gradeId);
//查询用户总记录数
Integer countStudentInfo();
//根据id修改用户信息
Integer updateStudent(Student student);
//添加用户
Integer addStudent(Student student);
//删除
Integer deleteStudent(Student student);
}

Mapper.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!---mapper路径-->
<mapper namespace="cn.cyg.mapper.StudentMapper">
<!--增删改查标签-->
<select></select>
<update></update>
<insert></insert>
<delete></delete>
</mapper>

四、动态mysql

动态SQL是MyBatis的强大特性之一

if

1
2
3
4
5
6
7
8
9
10
11
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>

choose、when、otherwise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>

where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>

trim

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</trim>
</select>

set和trim

1
2
3
4
5
6
7
8
9
10
<update id="updateAuthorIfNecessary">
update Author
<trim prefix="SET" suffixOverrides=",">
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</trim>
where id=#{id}
</update>

foreach

​ 动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

1
2
3
4
5
6
7
8
9
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

bind

1
2
3
4
5
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
标签作用
if单条件分支,相当于java的if
choose、when、otherwise不想使用所有的条件,而只是想从多个条件中选择一个使用。相当于switch语句;如果都没有传入when标签,那么就会执行otherwise
where、set只会在子元素返回任何内容的情况下才插入where子句,而且where元素会自动去除and和or;set会自动去除 –> , <–
trim可以通过自定义trim元素来定制where和set;prefix前缀;prefixOverrides去除前缀;suffix后缀;suffixOverrides去除后缀
foreach对集合进行遍历(构建IN条件语句时)声明可以在元素体内使用集合项(item)和索引(index)变量。也允许指定开头与结尾的字符串以及集合迭代之间的分隔符。当使用Map对象时,index是键,item是值
bind允许在OGNL表达式以外创建一个变量,并将其绑定到当前的上下文 name;value

OGNL表达式

Object Graphic Navigation Language(对象 图 导航 语言)==对象导航图语言

语法:#{}

参数

1.parameterType(输入类型)

​ 1)传递简单类型

​ 2)传递pojo对象

​ Mybatis使用ognl表达式解析对象字段的值,#{}或者${}中的值为pojo属性名称

​ 3)传递pojo包装对象

​ 开发中通过pojo传递查询条件,不仅包括用户查询条件还包括其他的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数

2.resultType(输出类型)

​ 1)输出简单类型

image-20211111190924176

​ 2)输出pojo对象

1
2
3
<select id="queryUserById" resultType="User">
select * from user where id=#{id}
</select>

​ 3)输出pojo列表

image-20211111191027548

3.resultMap结果类型

​ resultType可以指定将查询结果映射为pojo,但是需要pojo的属性名和sql查询的列名一致方可映射成功

​ 如果sql查询字段名和pojo属性名不一致,可以通过resultMap将字段名和属性名作为一个对应关系,resultMap实质上还需要将查询结果映射到pojo对象中

​ resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询

返回列名和实体类属性不一致

解决方案一:别名

1
2
3
<select id="getAllUser" resultType="User">
select id,userName name from user
</select>

解决方案二:使用resultMap

优势:开发效率高,降低代码耦合度

1
2
3
4
5
6
7
<resultMap id="uesrList" type="user">
<id property="id" column="id"></id>
<result column="userName" property="name"></result>
</resultMap>
<select id="getAllUser" resultMap="userList">
select * from user
</select>

多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。比如下面的例子:

1
2
3
4
5
6
7
8
9
10
11
<insert id="insert">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})
</insert>

五、Mybatis中的多表查询

1.一对一查询

1
2
3
4
5
6
7
8
9
10
11
12
<resultMap id="aMap" type="Account">
<id property="id" column="id"/>
<result property="uId" column="uId"/>
<result column="money" property="money"/>
<!--一对一的映射关系:配置封装user的内容-->
<association property="user" javaType="User" column="uId">
<id column="userId" property="id" />
<result property="sex" column="sex"/>
<result property="userName" column="userName"/>
<result property="birthday" column="birthday"/>
</association>
</resultMap>

2.一对多查询

用户信息和账户信息为一对多关系,如果用户没有账户,也要将该用户查出来,所以此时使用左外连接查询

一对多关系映射,主表实体包含从表实体的集合引用

1
2
3
4
5
6
7
8
9
10
11
12
<resultMap id="uMap" type="User">
<id property="id" column="id"/>
<result property="sex" column="sex"/>
<result column="birthday" property="birthday"/>
<result column="userName" property="userName"/>
<!--配置user对象中account集合的映射-->
<collection property="accountList" ofType="Account">
<id column="aId" property="id"/>
<result column="money" property="money"/>
<result column="uId" property="uId"/>
</collection>
</resultMap>

3.多对多查询

一个用户可以有多个角色,一个角色可以赋予多个用户

RoleMapper.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<resultMap id="rMap" type="Role">
<id property="id" column="id"/>
<result property="roleName" column="roleName"/>
<result column="roleDesc" property="roleDesc"/>
<collection property="userList" ofType="User">
<id column="uId" property="id"/>
<result column="userName" property="userName"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
</collection>
</resultMap>
<select id="getAllRoleAndUser" resultMap="rMap">
SELECT r.*, u.id uId, u.userName, u.sex, u.birthday
FROM role r
LEFT JOIN user_role ur ON r.id = ur.rid
LEFT JOIN USER u ON u.id = ur.uId
</select>

UserMapper.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<resultMap id="uMap1" type="User">
<id property="id" column="id"/>
<result property="sex" column="sex"/>
<result column="birthday" property="birthday"/>
<result column="userName" property="userName"/>
<collection property="roleList" ofType="Role">
<id column="rId" property="id"/>
<result column="roleName" property="roleName"/>
<result column="roleDesc" property="roleDesc"/>
</collection>
</resultMap>
<select id="getAllUser1" resultMap="uMap1">
SELECT u.*, r.roleName, r.id rId, r.roleDesc
FROM USER u
LEFT JOIN user_role ur ON ur.uId = u.id
LEFT JOIN role r ON ur.rId = r.id
</select>

六、Mybatis延迟加载

一、概述

1.mybatis中的延迟加载,也称为懒加载

2.是指在进行关联查询时,按照设置延迟规则对关联对象的select查询。

延迟加载可以有效的减少数据库的压力

3.延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓的延迟加载就是挡在真正需要的时候,才真正执行数据的加载

二、关联对象加载时机

  • 直接加载:

    ​ 执行完对主加载对象的select语句,马上执行对关联对象的select查询

  • 侵入式加载:

    ​ 侵入式延迟加载,执行主对象属性时,会走关联对象SQL语句,如果不访问主对象的属性,则不会走关联对象

  • 深度延迟加载:

    ​ 执行对主加载对象的查询时,不会执行对关联对象的查询,访问主加载对象的详情时,也不会执行关联对象的select查询,只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。

    访问主对象的属性,只执行了主对象的sql操作,并没有走关联对象的sql语句

  • 延迟加载的应用要求:

关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能使用多表连接所进行的select查询

三、直接加载、侵入式加载、深度加载

1.核心配置文件

image-20211111202620289

2.测试

直接加载:

image-20211114161147888

侵入式加载:

image-20211114161201219

深度延迟加载:

image-20211114161217185

四、总结

延迟加载:

  • 延迟加载对主对象都是直接加载,只有对关联对象时延迟加载
  • 延迟加载可以减轻数据库压力
  • 延迟加载不可以是一条SQL查询多表信息,这样构不成延迟加载,会形成直接加载

七、Mybatis中的缓存

1.什么是缓存?

缓存就是数据交换的缓冲区(Cache),是存储数据(使用频繁的数据)的临时空间

当要读取数据时,会首先从缓存查询数据,有就直接执行,不存在时从内存/数据库中获取

默认情况下,mabatis只开启一级缓存

2.为什么使用缓存

减少和数据库交互的次数,提高执行效率

3.什么样的数据能使用缓存

适用于缓存:

​ 1)经常查询并且经常使用

​ 2)数据的正确与否对最终结果影响不大

不适用于缓存:

​ 1)经常改变的数据

​ 2)数据的正确与否对最终结果影响很大

​ 3)比如:商品的库存,银行汇率,股票价格

Mybatis的一级缓存

  • 它指的是Mybatis中SqlSession对象的缓存
  • 同一个SqlSession对象,在参数和SQL完全一样的情况下,只执行一次SQL语句
  • 当我们执行查询之后,查询的结果会同时存入到SqlSession提供一块区域,该区域的结构是一个Map,当我们再次查询同样的数据,mybatis会先去SqlSession中查询,如果有直接使用;当SqlSession消失时,mybatis的一级缓存也就消失了

清空一级缓存的方法:

  1. 关闭SqlSession对象==>sqlSession.close()
  2. 调用SqlSession对象的clearCache()方法==>sqlSession.clearCache()
  3. 调用增删改方法

Mybatis的二级缓存

​ 它指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存

二级缓存开关

全局开关核心配置

image-20211111205929805

分开关
  1. Mapper.xml文件中配置

    让当前的映射文件支持二级缓存

    image-20211111210828079

  2. select标签中配置

    让当前的操作支持二级缓存

flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存

useCache默认为true,表示会将本条语句的结果进行二级缓存。

image-20211111210711930