MyBatis

特性

  • MyBatis 支持定制化 SQL、存储过程以及高级映射(字段和属性不一致,一对多或多对一映射)
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及结果集解析操作
  • MyBatis 可以使用简单的 XML 或注解实现配置和原始映射;将接口和 Java 的 POJO(Plain Ordinary Java Object,普通的 Java 对象)映射成数据库中的记录
  • Mybatis 是一个半自动的 ORM(Object Relation Mapping)框架(对象-关系映射)

配置

Maven 导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>

<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
</dependencies>

准备配置文件

Mybatis 全局配置文件

习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略。

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
<?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>

<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">

<!-- Mybatis的内置的事务管理器 -->
<transactionManager type="JDBC"/>

<!-- 配置数据源 -->
<dataSource type="POOLED">

<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="atguigu"/>
</dataSource>
</environment>
</environments>

<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="mappers/EmployeeMapper.xml"/>
</mappers>
</configuration>

存放的位置是 src/main/resources 目录下

Mapper 接口

Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类。

image

1
2
3
4
5
public interface EmployeeMapper {

Employee selectEmployee(Integer empId);

}

Mybatis 映射文件

相关概念:ORM(Object Relationship Mapping)对象关系映射。

  • 对象:Java 的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系

下表列举的是最简单的单表映射(一个表和一个类):

Java 概念 数据库概念
属性 字段/列
对象 记录/行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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是根标签,namespace属性:在Mybatis全局范围内找到一个具体的Mapper配置 -->
<!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 -->
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">

<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
<!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select>
</mapper>

注意:EmployeeMapper.xml 所在的目录要和 mybatis-config.xml 中使用 mapper 标签配置的一致

一张表——–> 实体类 —-> 对应当前的 mapper 接口—-对应一个映射文件 *mapper 接口中的方法—–> 对应映射文件中的 SQL 语句

添加测试

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
@Test
public void testSelectEmployee() throws IOException {

// 1.创建SqlSessionFactory对象
// ①声明Mybatis全局配置文件的路径
String mybatisConfigFilePath = "mybatis-config.xml";

// ②以输入流的形式加载Mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream(mybatisConfigFilePath);

// ③基于读取Mybatis配置文件的输入流创建SqlSessionFactory对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 2.使用SqlSessionFactory对象开启一个会话
SqlSession session = sessionFactory.openSession();

// 3.根据Mapper配置文件的名称空间+SQL语句的id找到具体的SQL语句
// 格式是:名称空间.SQL语句的id
String statement = "com.atguigu.mybatis.dao.EmployeeMapper.selectEmployee";

// 要传入SQL语句的参数
Integer empId = 1;

// 执行SQL语句
Object result = session.selectOne(statement, empId);

System.out.println("o = " + result);

// 4.关闭SqlSession
session.close();
}
  • SqlSession:代表 Java 程序和数据库之间的会话。(HttpSession 是 Java 程序和浏览器之间的会话)
  • SqlSessionFactory:是“生产”SqlSession 的“工厂”。
  • 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testUsrMapperInterface() {
补充测试方法
// 1.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);

// 2.调用EmployeeMapper接口的方法完成对数据库的操作
Emp emp = employeeMapper.selectEmployee(1L);

// 3.打印查询结果
System.out.println("emp = " + emp);
}

关闭前最后提交事务:sqlSession.commit();

另外还有一仲不使用接口的方式,sqlSession 可以调用如 insert 等方法,接受参数为 namespace.sqlId;

image

在获取 sqlSession 时 SqlSession session = sessionFactory.openSession();​,这个方法接受一个参数来设置自动提交,true表示自动提交;

其他的 update 等都是类似的,这时我们的 sql 还都是静态的;

Log4j

日志级别

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试),从左到右打印的内容越来越详细

STDOUT

是 standard output 的缩写,意思是标准输出。对于 Java 程序来说,打印到标准输出就是打印到控制台。

查询

代码:

1
2
3
4
5
  <select id="getUserById">
select * from t_user where id = 1;
</select>

User getUserById();

这时我们考虑返回值如何拿到;我们要通过 ResultMap/Type 来解决;要在 select 标签中声明 ResultType;(Result 是自定义映射)

1
2
3
<select id="getUserById" resultType="com.hkk.mybatis.pojo.User">
select * from t_user where id = 1;
</select>

ResultType,查询结果为 java 对应类型;

如果是多行查询,但是 java 中有对应实体类,那么,resultType 也还是 list 中的元素的类型而不是 list 类型,比如:

1
2
3
<select id="getUserById" resultType="com.hkk.mybatis.pojo.User">
select * from t_user where id >=1;
</select>

目前这样的查询只会返回一个对象,想要返回 list 还需要修改;

核心配置文件

environment

用于设置环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">

<!-- Mybatis的内置的事务管理器 类型一个是JDBC一个是MANAGED-->
jdbc是原生管理,比如手动设置提交等
mannaged是被管理的,比如后继用Spring管理;
<transactionManager type="JDBC"/>

<!-- 配置数据源 -->
这个type设置数据源类型,有POLLED,UNPOLLED,JNDI
分别表示数据库连接池,非连接池, 使用上下文的数据源
==================
<dataSource type="POOLED">

<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="abc123"/>
</dataSource>
</environment>
</environments>

Properties

1
2
3
4
5
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="abc123"/>

这一段内容可以用 properties 文件配置;

jdbc.properties:

1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&amp;characterEncoding=utf8
jdbc.username=root
jdbc.password=abc123

引入配置:在核心配置中

1
2
3
4
5
<configuration>

<properties resource="jdbc.properties"/>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">

这样,我们就可以在核心配置文件中用 ${key}的方式访问 value

总之 properties 标签的作用就是引入 properties 文件;

typeAliases 类型别名

比如各种全类名(resultType 等);

<typeAlias type="原类名" alisa="别名">

作用范围包括核心配置文件,sql 语句等;

当只设置 type 而不设置 alisa 时,会给一个默认别名,比如 com.xxx.user,那么他的别名默认就是 user 和 User,不区分大小写

在 typeAliases 标签里还有一个 标签,属性 name,name 就是包名,表示这个包下面的所有类都会被赋默认别名

Mapper

mapper 和别名类似,也可以以包为单位设置映射文件引入;但需要满足:

  1. mapper 接口和映射文件必须在同一个包下
  2. 接口名和对应映射文件名必须相同

image

resources 这个 mapper 一定要和 java 上面的个 mapper 一致

获取参数

#{}方式

Mybatis 会在运行过程中,把配置文件中的 SQL 语句里面的#{}转换为“?”占位符,发送给数据库执行。

配置:

1
2
3
<delete id="deleteEmployeeById">
delete from t_emp where emp_id=#{empId}
</delete>

实际执行的就是:

1
delete from t_emp where emp_id=?

${}方式

和#{}不同,这仲方式会产生 SQL 注入的问题,原理就是字符串拼接

1
2
3
 <select id="selectEmployeeByName" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_name like '%${empName}%'
</select>

获取参数的几种情况

简单类型的单参数

1
2
3
4
5
<select id="getUserByUsername" resultType="com.hkk.mybatis.pojo.User">
select * from t_user where username = #{username};
</select>

User getUSerByUsername(String username);

#{username}配置文件中的这里,括号内的形参名和查询操作是无关的,但一般建议同名;

另外的,使用 ${ }时除了 sql 注入问题,还要注意引号,比如 String,Date 在 SQL 查询时应该加上引号,如‘${xxxxx}’,但是#是占位符形式,则无需关注引号了;总之一般情况下,能#就#,不能才$;

简单类型的多参数

和单参数不同,多参数的 mybatis 的 SQL 语句里的参数需要使用下面的方法调用

1
2
3
4
<select id="checkLogin" resultType="user">
select * from t_user where username= #{arg0}
and password = #{arg1};
<select>

这样调用时,输入的两个参数就是按照 0 开始,按顺序放入 sql 语句中;

另外也可以使用 param1 到 2,3 等来调用;

1
2
3
4
<select id="checkLogin" resultType="user">
select * from t_user where username= #{param1}
and password = #{param2};
<select>

这是由于 mabatis 的参数是由一个 map 存放的,以两种方式 argx 和 paramx 为 key,value 为实际参数;arg0,arg1….param1,param2….

以 map 作为参数

接口方法,传入一个 map

1
User checkLoginByMap(Map<String,Object> map);

配置:

1
2
3
<select id="checkLoginByMap" resultType="com.hkk.mybatis.pojo.User">
select * from t_user where username = #{username} and password = #{password}
</select>

当调用时,只需要传入一个 map,包含两个元素,分别以 username 和 password 为 key,那么这样这个 sql 语句就可以调用到这里面的 value 了;

实体类型参数

在 mybatis 里,所谓实体类的属性,实际上是 get/set 方法去掉前缀后剩余的部分,转为小写;比如 getId,对应 id 这个属性;只和 get 和 set 方法有关;比如

1
2
3
public void getId(){
return name;
}

在这个 get 方法里,返回的是类的 name 成员变量,但是作为属性,这个 name 就是 id 这个属性;

1
2
3
4
<insert id="insertUser">
insert into t_user (id, username, password, age, gender, email)
values (null,#{username},#{password},#{age},#{gender},#{email});
</insert>

这里的#{username}等,就会调用传入对象的 getxxx 方法,获得一个值,填充到 sql 里;

注解获取参数

java 接口

1
User checkLoginByParam(String username,String password);

我们可以在这个接口上加上注解:

1
2
3
User checkLoginByParam(
@Param("username")String username,
@Param("passworld")String password);

@Param(xxx)这个注解会把注解内容xxx作为底层map的key,这样,就在配置里,#{xxx}就可以直接找到对应的value,这使我们可以自定义map

查询返回

单个简单类型

1
2
3
<select id="selectEmpCount" resultType="int">
select count(*) from t_emp
</select>

非常常规,调用返回一个int;

Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。

实体类

1
User getUserById(@Param("id") Integer id);

习惯给方法加上注解

配置

1
2
3
<select id="getUserById" resultType="user">
select * from t_user where id = #{id}
<select>

在查询时,如果查询的是一个实体类对象,那么就会返回一个user对象,如果是多个结果且都属于一个实体类,那么就返回一个list;

注意,返回值为多的时候,接口的返回值就应该是List,用User类尝试去接收List会导致报错;

增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可

Map

适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。

1
Map<String,Object> selectEmpNameAndMaxSalary();
1
2
3
4
5
6
7
8
9
10
11
<!-- Map<String,Object> selectEmpNameAndMaxSalary(); -->
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>

这样返回的就是一个map,在这个map里,key为字段名,value为实际的值,当然这里只是单行查询的情况;当查询返回null为value时,null不会被加入到map中;

在多条map时, 这样回报错,也就是说多条数据不能直接塞到同一个map里;可以用List集合接收;

1
List<Map<String,Object>> getAllUserToMap();
1
mapper.getAllUserToMap();

返回一个List,List里又包含多个Map;

或者,我们会使用MapKey注解,用Map来放Map,通过mapkey来指定一个属性作为key;

1
2
@MapKey("id")
Map<String,Object> getAllUserToMap();

这时输出的大map,key为id,value为一个map,这个map里也包含了这个id;

image

这个大key要注意不可重复性;

模糊查询

image

都是拼接字符串实现,其中使用较多的是

1
select * from xxx where xxx like "%"#{xxx}"%"

用添加功能获取自增主键

例如功能,有两表clazz班级表和student学生表;要实现这样一个功能:

  1. 添加班级
  2. 获取新添加的班级id(id是自增主键,不是我们输入的)
  3. 为班级分配学生,也就是把学生的班级id修改为新添加的般的的id;
1
void insertUser(User user);
1
2
3
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into 。。。常规sql
<insert>

这里涉及到两个参数:useGeneratedKeys=”true” 和keyProperty=”id”,前者表示”允许返回自增主键的值”,至于后者,首先jdbc中增删改的返回值一定是修改数据的行数,至于如何拿到这个主键,则需要通过传入对象来获取;

比如,user需要id,gender,name这三个属性,而id自增,则调用时只需要传入null,性别,姓名这样的参数,在方法调用后,null就会修改为自增主键的值,执行前无id,执行后有id;实际上赋值给哪个参数都是允许的;

自定义映射

全局配置

SQL表的列名和类的属性名不一致时,需要创建自定义映射;传统的,可以使用别名的方式解决;

一般SQL的列名是xxx_xxx的命名规则,而java中的属性,一般是xxxXxx的命名;Mybatis的核心配置里<setting>可以设置一个全局配置,可以自动把下划线命名映射到驼峰命名;

<seeting name=”mapUdenerscoreToCamelCase” value=”true”/>

比如emp_id就会映射为empId,user_name就是userName;

resultMap

使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系,注意这里对于主键属性和非主键属性的处理是不一样的;(id和colum)

在配置文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">

<!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
<!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
<id column="emp_id" property="empId"/>

<!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
<result column="emp_name" property="empName"/>
<result column="emp_salary" property="empSalary"/>
</resultMap>

<!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>

这样返回时返回的就是特定的resultMap, 对于结果,key就是指定的id,value就是上面的属性,最后返回的就是<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee"> ​,这里所对应的类Employee;

关联关系

数量和方向

  1. 数量关系

    主要体现在数据库表中

    一对一

    夫妻关系,人和身份证号

    一对多
    用户和用户的订单,锁和钥匙

    多对多
    老师和学生,部门和员工

  2. 方向

    主要体现在Java实体类中

    • 双向:双方都可以访问到对方

      • Customer:包含Order的集合属性
      • Order:包含单个Customer的属性
    • 单向:双方中只有一方能够访问到对方

      • Customer:不包含Order的集合属性,访问不到Order
      • Order:包含单个Customer的属性

简单来说,一对一对应的就是一个对象,一对多对应的就是一个集合;

多对一

比如Emp员工类包含几个属性:empId,empName,age,gender和一个Dept类型的部门属性,但是就表结构来说,通过join查询得到的数据是empId,empName,age,gender和Dpet表获取的deptName共五个数据,那么如何把deptName属性给到Dept类型呢;

SQL

image

表结构

image

结果例

image

image

image

也就是说,dept_name显然和Dept这个class是不对应的,要解决的就是如何将字段直接映射成对象属性的问题

级联

必须使用resultMap处理;处理Emp类型的映射

1
2
3
4
5
6
7
8
9
10
11
<resultMap id="empAndDeptResultMap"
type="Emp">
Emp本身的属性映射略,重点关注Dept

<result column="dept_id" property="dept.deptId">
<result>
这里的dept就是Emp这个类内的一个属性,
也就是Dept类-dept,通过dept来调其中的属性dept_id;
dept_name也是同理的

</resultMap>

image

这样,结果就可以查询出来了(注意先重写toString方法)

image

切记,这里的resultMap是映射给Emp,给Emp内的特殊属性Dept,再调用内部的属性映射;

association

用于处理实体类类型的属性,包括一对一,多对一;

1
2
3
4
5
6
7
8
9
<association property="dept" 指定sql查询出来的字段名
javaType="Dept">
映射到的java实体类,也就是属性的类型

<id column = "dept_id" property="deptId"></id>
主键-dept_id指向为Dept类型下的deptId属性;
<result column = "dept_name" property="deptName">
非主键属性,映射;
</association>

这就是association的配置

image

分步查询

首先,第一个sql

1
2
3
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
select * from t_emp where emp_id = #{empId}
</select>

和常规的select一样,返回一个结果集,重点就在于这个结果集的配置,在这个sql里,查询到的是emp表的所有信息;

1
2
3
4
5
6
7
8
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<association property="dept" select="" column="">

</association>

在resultmap的association标签里,又引入了几个新属性:

1
select="" column=""

select绑定第二个sql语句,比如:

1
2
3
4
5
6
7
Dpet getEmpAndDpetByStepTwo(@Param("deptId")
Integer deprId); 接口方法

<select id="getEmpAndDpetByStepTwo" resultType=
"Dept" 这里当然也可以配置resultMap>
select * from t_dept where dept_id =#{deptId}
</select>

关联俩个sql,在之前的sql-asscociation里:

1
2
3
4
5
6
<association property="dept" 
select="com.xxx.getEmpAndDpetByStepTwo"
这里select要通过全类名.xxxMapper.方法名定位
column="dept_id"
这里的column写法的是列名也就是第一个sql给下一个sql的条件
>

总之,整个过程就是,第一条sql:select * from t_emp where emp_id = #{empId} ​查询出来了emp的数据,在association中配置,调用第二条sql,指定返回值为属性dept,同时把第一条sql的结果中的dept_id作为参数给到第二条sql;select * from t_dept where dept_id =#{deptId} ​在resultMap里映射了他返回Dept类型,把Dept类型最后给到dept属性;./images

延迟加载

比如查询到Customer的时候,不一定会使用Order的List集合数据。如果Order的集合数据始终没有使用,那么这部分数据占用的内存就浪费了。对此,我们希望不一定会被用到的数据,能够在需要使用的时候再去查询

例如:对Customer进行1000次查询中,其中只有15次会用到Order的集合数据,那么就在需要使用时才去查询能够大幅度节约内存空间

延迟加载的概念:对于实体类关联的属性到需要使用时才查询。也叫懒加载。

配置

1
2
3
4
5
6
7
8
9
<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
<!-- 开启延迟加载功能:需要配置两个配置项 -->
<!-- 1、将lazyLoadingEnabled设置为true,开启懒加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>

<!-- 2、将aggressiveLazyLoading设置为false,关闭“积极的懒加载” -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

在高版本中,则

1
2
3
4
5
6
<!-- Mybatis全局配置 -->
<settings>
<!-- 开启延迟加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
ggressiveLazyLoading在高版本默认就为false;
</settings>

那么在使用时,比如上面的emp和dept,如果我们查询了一个emp结果,但是在之后的的代码里没有对emp的dept这个属性操作,那么就只会执行第一条sql而不会执行第二条sql,反之,如果需要dept属性,则又会执行二条sql;总之就是先查询第一个sql本身,在需要内部某特殊属性时再调用第二条sql查询;

当然,也可以对某一条sql设置延迟加载而不使用全局配置,比如:fatchType=“eager/lazy”,在分布查询的连接association里使用这个属性来设置是否开启这组分布查询sql的延迟加载,默认遵循全局配置;

1
2
3
4
5
6
7
8
<association property="dept" 
fatchType=“eager”

select="com.xxx.getEmpAndDpetByStepTwo"
这里select要通过全类名.xxxMapper.方法名定位
column="dept_id"
这里的column写法的是列名也就是第一个sql给下一个sql的条件
>

一对多

collection

类似association;区别在于collection处理一对多的关系,和association内部关注ofType属性而不是javaType属性

image

image

分步

第一个sql

image

image

第二个sql

image

组合

image

动态SQL

Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。

If和Where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp

<!-- where标签会自动去掉“标签体内前面多余的and/or” -->
<where>
<!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
<!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
<if test="empName != null">
<!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
or emp_name=#{empName}
</if>
<if test="empSalary &gt; 2000">
or emp_salary>#{empSalary}
</if>
<!--
第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
第二种情况:部分条件满足 WHERE emp_salary>?
第三种情况:所有条件都不满足 没有where子句
-->
</where>
</select>

重点,若包含满足的条件,where标签会生成where关键字并且自动去掉标签体内前面多余的and/or;

trim

使用trim标签控制条件部分两端是否包含某些字符

  • prefix属性:指定要动态添加的前缀
  • suffix属性:指定要动态添加的后缀
  • prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
  • suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
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
<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_age,emp_salary,emp_gender
from t_emp

<!-- prefix属性指定要动态添加的前缀 -->
<!-- suffix属性指定要动态添加的后缀 -->
<!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
<!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
<!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null">
emp_name=#{empName} and
</if>
<if test="empSalary &gt; 3000">
emp_salary>#{empSalary} and
</if>
<if test="empAge &lt;= 20">
emp_age=#{empAge} or
</if>
<if test="empGender=='male'">
emp_gender=#{empGender}
</if>
</trim>
</select>

choose/when/otherwise

在多个分支条件中,仅执行一个。

  • 从上到下依次执行条件判断
  • 遇到的第一个满足条件的分支会被采纳
  • 被采纳分支后面的分支都将不被考虑
  • 如果所有的when分支都不满足,那么就执行otherwise分支,类似于if-else;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary &lt; 3000">emp_salary &lt; 3000</when>
<otherwise>1=1</otherwise>
</choose>

<!--
第一种情况:第一个when满足条件 where emp_name=?
第二种情况:第二个when满足条件 where emp_salary < 3000
第三种情况:两个when都不满足 where 1=1 执行了otherwise
-->
</select>

set

实际开发时,对一个实体类对象进行更新。往往不是更新所有字段,而是更新一部分字段。此时页面上的表单往往不会给不修改的字段提供表单项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form action="" method="">

<input type="hidden" name="userId" value="5232" />

年 龄:<input type="text" name="userAge" /><br/>
性 别:<input type="text" name="userGender" /><br/>
坐 标:<input type="text" name="userPosition" /><br/>
<!-- 用户名:<input type="text" name="userName" /><br/> -->
<!-- 余 额:<input type="text" name="userBalance" /><br/>-->
<!-- 等 级:<input type="text" name="userGrade" /><br/> -->

<button type="submit">修改</button>

</form>

例如上面的表单,如果服务器端接收表单时,使用的是User这个实体类,那么userName、userBalance、userGrade接收到的数据就是null。

如果不加判断,直接用User对象去更新数据库,在Mapper配置文件中又是每一个字段都更新,那就会把userName、userBalance、userGrade设置为null值,从而造成数据库表中对应数据被破坏。

此时需要我们在Mapper配置文件中,对update语句的set子句进行定制,此时就可以使用动态SQL的set标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
update t_emp
<!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
<!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
<set>
<if test="empName != null">
emp_name=#{empName},
</if>
<if test="empSalary &lt; 3000">
emp_salary=#{empSalary},
</if>
</set>
where emp_id=#{empId}
<!--
第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
第二种情况:部分条件满足 SET emp_salary=?
第三种情况:所有条件都不满足 update t_emp where emp_id=?
没有set子句的update语句会导致SQL语法错误
-->
</update>

foreach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--
collection属性:要遍历的集合
item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
index属性:这里起一个名字,便于后面引用
遍历List集合,这里能够得到List集合的索引值
遍历Map集合,这里能够得到Map集合的key
-->
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
<!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
(#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach>

image

image

批量更新时需要注意

上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:

1
atguigu.dev.url=jdbc:mysql://192.168.198.100:3306/mybatis-example?allowMultiQueries=true
1
2
3
4
5
6
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch">
<foreach collection="empList" item="emp" separator=";">
update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}
</foreach>
</update>

SQL标签

抽取重复的SQL片段

1
2
3
4
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>
1
2
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>

使用

image

缓存

  • 一级缓存:SqlSession级别
  • 二级缓存:SqlSessionFactory级别

一级缓存

一级缓存失效的情况

  • 不是同一个SqlSession
  • 同一个SqlSession但是查询条件发生了变化
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存
  • 同一个SqlSession两次查询期间提交了事务

二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查间的结果会被缓存;

此后,若再次执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件:

  1. 在核心配置文件中,设置全局配置属性cacheEnabled正”true”,默认为true,不需要设置
  2. 在映射文件中设置标签
  3. 二级缓存必须在SqlSession关闭或提交之后有效
  4. 查询的数据所转换的实体类类型必须实现序列化的接口

使二级缓存失效的情况:
两次查询之间执行了任意的增册改,会使一级和二级缓存同时失效

二级缓存相关配置

在Mapper配置文件中添加的cache标签可以设置一些属性:

  • eviction属性:缓存回收策略
    LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
    FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    默认的是 LRU。
  • flushInterval属性:刷新间隔,单位毫秒
    默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
  • size属性:引用数目,正整数
    代表缓存最多可以存储多少个对象,太大容易导致内存溢出
  • readOnly属性:只读,true/false
    true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
    false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

查询顺序

  1. 二级缓存
  2. 一级缓存
  3. 数据库

*第三方缓存EhCache

  1. 导入Maven依赖

    jar包名称 作用
    mybatis-ehcache Mybatis和EHCache的整合包
    ehcache EHCache核心包
    slf4j-api SLF4J日志门面包
    logback-classic 支持SLF4J门面接口的一个具体实现
  2. 创建EHCache配置文件

    image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?xml version="1.0" encoding="utf-8" ?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\atguigu\ehcache"/>

    <defaultCache
    maxElementsInMemory="1000"
    maxElementsOnDisk="10000000"
    eternal="false"
    overflowToDisk="true"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    diskExpiryThreadIntervalSeconds="120"
    memoryStoreEvictionPolicy="LRU">
    </defaultCache>
    </ehcache>
  3. 指定缓存管理器的具体类型

    还是到查询操作所在的Mapper配置文件中,找到之前设置的cache标签:

    1
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  4. 日志

    存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。

    门面:

    名称 说明
    JCL(Jakarta Commons Logging) 陈旧
    SLF4J(Simple Logging Facade for Java)★ 适合
    jboss-logging 特殊专业领域使用

    实现:

    名称 说明
    log4j★ 最初版
    JUL(java.util.logging) JDK自带
    log4j2 Apache收购log4j后全面重构,内部实现和log4j完全不同
    logback★ 优雅、强大
  5. logback配置文件

    image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
    class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
    <!-- 日志输出的格式 -->
    <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
    <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
    </encoder>
    </appender>

    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
    <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
    <appender-ref ref="STDOUT" />
    </root>

    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="com.atguigu.crowd.mapper" level="DEBUG"/>

    </configuration>
  6. EHCache配置文件说明

    当借助CacheManager.add(“缓存名称”)创建Cache时,EhCache便会采用指定的的管理策略

    属性名 是否必须 作用
    maxElementsInMemory 在内存中缓存的element的最大数目
    maxElementsOnDisk 在磁盘上缓存的element的最大数目,若是0表示无穷大
    eternal 设定缓存的elements是否永远不过期。
    如果为true,则缓存的数据始终有效,
    如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
    overflowToDisk 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
    timeToIdleSeconds 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,
    这些数据便会删除,默认值是0,也就是可闲置时间无穷大
    timeToLiveSeconds 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
    diskSpoolBufferSizeMB DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
    diskPersistent 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
    diskExpiryThreadIntervalSeconds 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,
    相应的线程会进行一次EhCache中数据的清理工作
    memoryStoreEvictionPolicy 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。
    默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

逆向工程

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。

  • 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:

    • Java实体类
    • Mapper接口
    • Mapper配置文件

生成逆向工程

Maven配置:

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
39
40
41
42
43
44
45
46
47
48
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>

<!-- 控制Maven在构建过程中相关配置 -->
<build>

<!-- 构建过程中用到的插件 -->
<plugins>

<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>

<!-- 插件的依赖 -->
<dependencies>

<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>

<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>

<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

MBG配置

文件名必须是:generatorConfig.xml

image

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://192.168.198.100:3306/mybatis-example"
userId="root"
password="atguigu">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.atguigu.mybatis.entity" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Employee"/>
<table tableName="t_customer" domainObjectName="Customer"/>
<table tableName="t_order" domainObjectName="Order"/>
</context>
</generatorConfiguration>

注意<jdbcConnection>的修改和目标路径的配置 <javaModelGenerator targetPackage="com.atguigu.mybatis.entity" targetProject=".\src\main\java">

maven-执行MBG插件的generate目标
image

QBC查询

QBC查询最大的特点就是将SQL语句中的WHERE子句进行了组件化的封装,让我们可以通过调用Criteria对象的方法自由的拼装查询条件。

对于自动生成的条件查询,需要使用QBC查询去调用;

类似这样的方法,需要传入一个xxxExample的:

1
List<Dept> selectByExample(DeptExample example);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="selectByExample" resultMap="BaseResultMap" parameterType="com.atguigu.mybatis.entity.DeptExample" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Tue Jan 17 23:29:02 CST 2023.
-->
select
<if test="distinct" >
distinct
</if>
<include refid="Base_Column_List" />
from t_dept
<if test="_parameter != null" >
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null" >
order by ${orderByClause}
</if>
</select>

QBC查询例,也就是通过创建xxxExample的对象,通过这个对象再去创建Criteria对象,通过Criteria对象就可以给SQL语句添加条件;添加的条件andXxxxx可以一直追加;

那么对于or的连接,

image

也只需要再次掉用example对象,调用or方法后继续调用andXxx方法,即相当于xxx and xxx or xxx and xxx这样的形式;

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
// 1.创建EmployeeExample对象
EmployeeExample example = new EmployeeExample();

// 2.通过example对象创建Criteria对象
EmployeeExample.Criteria criteria01 = example.createCriteria();
EmployeeExample.Criteria criteria02 = example.or();

// 3.在Criteria对象中封装查询条件
criteria01
.andEmpAgeBetween(9, 99)
.andEmpNameLike("%o%")
.andEmpGenderEqualTo("male")
.andEmpSalaryGreaterThan(500.55);

criteria02
.andEmpAgeBetween(9, 99)
.andEmpNameLike("%o%")
.andEmpGenderEqualTo("male")
.andEmpSalaryGreaterThan(500.55);

SqlSession session = factory.openSession();

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

// 4.基于Criteria对象进行查询
List<Employee> employeeList = mapper.selectByExample(example);

for (Employee employee : employeeList) {
System.out.println("employee = " + employee);
}

session.close();

// 最终SQL的效果:
// WHERE ( emp_age between ? and ? and emp_name like ? and emp_gender = ? and emp_salary > ? ) or( emp_age between ? and ? and emp_name like ? and emp_gender = ? and emp_salary > ? )

分页插件

limit index,pageSize

pageSize:每页显示的条数

pageNum:当前页的页码

index:当前页的第一条数据的索引,index=(pageNum-1)*pageSize

count:总记录数

totalPage:总页数

totalPage = count / pageSize;

解决最后一页的,数据不足一页情况:

if(count % pageSize != 0){

totalPage += 1;

}

pageSize=4,pageNum=1,index=0 limit 0,4

pageSize=4,pageNum=3,index=8 limit 8,4

pageSize=4,pageNum=6,index=20 limit 20,4

首页 上一页 2 3 4 5 6 下一页 末页

使用分页插件

添加依赖和添加插件

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>

在MyBatis的核心配置文件中配置插件

1
2
3
4
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

使用

依附于查询功能

  1. 在查询功能之前开启查询功能;

    使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能

    其中

    pageNum:当前页的页码

    pageSize:每页显示的条数

  2. 在查询获取list集合之后:

    使用PageInfo pageInfo = new PageInfo<>(List list, intnavigatePages)获取分页相关数据

    ist:分页之后的数据

    navigatePages:导航分页的页码数

PageHelper.startPage(int pageNum, int pageSize)​方法会返回一个page对象,这个对象包含page信息和内容信息;

image

PageInfo,T是查询的对象,这个方法接收一个list对象(也就是查询的list)和导航分页个数;

image

PageInfo内部的数据更多,如

PageInfo{

pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,

list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,

pages=8, reasonable=false, pageSizeZero=false},

prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,

hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,

navigatepageNums=[4, 5, 6, 7, 8]

}

pageNum:当前页的页码

pageSize:每页显示的条数

size:当前页显示的真实条数

total:总记录数

pages:总页数

prePage:上一页的页码

nextPage:下一页的页码

isFirstPage/isLastPage:是否为第一页/最后一页

hasPreviousPage/hasNextPage:是否存在上一页/下一页

navigatePages:导航分页的页码数

navigatepageNums:导航分页的页码,[1,2,3,4,5]