MyBatis
MyBatis
特性
- MyBatis 支持定制化 SQL、存储过程以及高级映射(字段和属性不一致,一对多或多对一映射)
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及结果集解析操作
- MyBatis 可以使用简单的 XML 或注解实现配置和原始映射;将接口和 Java 的 POJO(Plain Ordinary Java Object,普通的 Java 对象)映射成数据库中的记录
- Mybatis 是一个半自动的 ORM(Object Relation Mapping)框架(对象-关系映射)
配置
Maven 导入依赖
1 | <dependencies> |
准备配置文件
Mybatis 全局配置文件
习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略。
1 |
|
存放的位置是 src/main/resources 目录下
Mapper 接口
Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类。
1 | public interface EmployeeMapper { |
Mybatis 映射文件
相关概念:ORM(Object Relationship Mapping)对象关系映射。
- 对象:Java 的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
下表列举的是最简单的单表映射(一个表和一个类):
Java 概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
1 |
|
注意:EmployeeMapper.xml 所在的目录要和 mybatis-config.xml 中使用 mapper 标签配置的一致
一张表——–> 实体类 —-> 对应当前的 mapper 接口—-对应一个映射文件 *mapper 接口中的方法—–> 对应映射文件中的 SQL 语句
添加测试
1 |
|
- SqlSession:代表 Java 程序和数据库之间的会话。(HttpSession 是 Java 程序和浏览器之间的会话)
- SqlSessionFactory:是“生产”SqlSession 的“工厂”。
- 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
1 |
|
关闭前最后提交事务:sqlSession.commit();
另外还有一仲不使用接口的方式,sqlSession 可以调用如 insert 等方法,接受参数为 namespace.sqlId;
在获取 sqlSession 时 SqlSession session = sessionFactory.openSession();
,这个方法接受一个参数来设置自动提交,true表示自动提交;
其他的 update 等都是类似的,这时我们的 sql 还都是静态的;
Log4j
日志级别
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试),从左到右打印的内容越来越详细
STDOUT
是 standard output 的缩写,意思是标准输出。对于 Java 程序来说,打印到标准输出就是打印到控制台。
查询
代码:
1 | <select id="getUserById"> |
这时我们考虑返回值如何拿到;我们要通过 ResultMap/Type 来解决;要在 select 标签中声明 ResultType;(Result 是自定义映射)
1 | <select id="getUserById" resultType="com.hkk.mybatis.pojo.User"> |
ResultType,查询结果为 java 对应类型;
如果是多行查询,但是 java 中有对应实体类,那么,resultType 也还是 list 中的元素的类型而不是 list 类型,比如:
1 | <select id="getUserById" resultType="com.hkk.mybatis.pojo.User"> |
目前这样的查询只会返回一个对象,想要返回 list 还需要修改;
核心配置文件
environment
用于设置环境
1 | <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 --> |
Properties
1 | <!-- 建立数据库连接的具体信息 --> |
这一段内容可以用 properties 文件配置;
jdbc.properties:
1 | jdbc.driver=com.mysql.cj.jdbc.Driver |
引入配置:在核心配置中
1 | <configuration> |
这样,我们就可以在核心配置文件中用 ${key}的方式访问 value
总之 properties 标签的作用就是引入 properties 文件;
typeAliases 类型别名
比如各种全类名(resultType 等);
<typeAlias type="原类名" alisa="别名">
作用范围包括核心配置文件,sql 语句等;
当只设置 type 而不设置 alisa 时,会给一个默认别名,比如 com.xxx.user,那么他的别名默认就是 user 和 User,不区分大小写
在 typeAliases 标签里还有一个
Mapper
mapper 和别名类似,也可以以包为单位设置映射文件引入;但需要满足:
- mapper 接口和映射文件必须在同一个包下
- 接口名和对应映射文件名必须相同
resources 这个 mapper 一定要和 java 上面的个 mapper 一致
获取参数
#{}方式
Mybatis 会在运行过程中,把配置文件中的 SQL 语句里面的#{}转换为“?”占位符,发送给数据库执行。
配置:
1 | <delete id="deleteEmployeeById"> |
实际执行的就是:
1 | delete from t_emp where emp_id=? |
${}方式
和#{}不同,这仲方式会产生 SQL 注入的问题,原理就是字符串拼接
1 | <select id="selectEmployeeByName" resultType="com.atguigu.mybatis.entity.Employee"> |
获取参数的几种情况
简单类型的单参数
1 | <select id="getUserByUsername" resultType="com.hkk.mybatis.pojo.User"> |
#{username}配置文件中的这里,括号内的形参名和查询操作是无关的,但一般建议同名;
另外的,使用 ${ }时除了 sql 注入问题,还要注意引号,比如 String,Date 在 SQL 查询时应该加上引号,如‘${xxxxx}’,但是#是占位符形式,则无需关注引号了;总之一般情况下,能#就#,不能才$;
简单类型的多参数
和单参数不同,多参数的 mybatis 的 SQL 语句里的参数需要使用下面的方法调用
1 | <select id="checkLogin" resultType="user"> |
这样调用时,输入的两个参数就是按照 0 开始,按顺序放入 sql 语句中;
另外也可以使用 param1 到 2,3 等来调用;
1 | <select id="checkLogin" resultType="user"> |
这是由于 mabatis 的参数是由一个 map 存放的,以两种方式 argx 和 paramx 为 key,value 为实际参数;arg0,arg1….param1,param2….
以 map 作为参数
接口方法,传入一个 map
1 | User checkLoginByMap(Map<String,Object> map); |
配置:
1 | <select id="checkLoginByMap" resultType="com.hkk.mybatis.pojo.User"> |
当调用时,只需要传入一个 map,包含两个元素,分别以 username 和 password 为 key,那么这样这个 sql 语句就可以调用到这里面的 value 了;
实体类型参数
在 mybatis 里,所谓实体类的属性,实际上是 get/set 方法去掉前缀后剩余的部分,转为小写;比如 getId,对应 id 这个属性;只和 get 和 set 方法有关;比如
1 | public void getId(){ |
在这个 get 方法里,返回的是类的 name 成员变量,但是作为属性,这个 name 就是 id 这个属性;
1 | <insert id="insertUser"> |
这里的#{username}等,就会调用传入对象的 getxxx 方法,获得一个值,填充到 sql 里;
注解获取参数
java 接口
1 | User checkLoginByParam(String username,String password); |
我们可以在这个接口上加上注解:
1 | User checkLoginByParam( |
@Param(xxx)这个注解会把注解内容xxx作为底层map的key,这样,就在配置里,#{xxx}就可以直接找到对应的value,这使我们可以自定义map
查询返回
单个简单类型
1 | <select id="selectEmpCount" resultType="int"> |
非常常规,调用返回一个int;
Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。
实体类
1 | User getUserById(@Param("id") Integer id); |
习惯给方法加上注解
配置
1 | <select id="getUserById" resultType="user"> |
在查询时,如果查询的是一个实体类对象,那么就会返回一个user对象,如果是多个结果且都属于一个实体类,那么就返回一个list;
注意,返回值为多的时候,接口的返回值就应该是List,用User类尝试去接收List会导致报错;
增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
Map
适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
1 | Map<String,Object> selectEmpNameAndMaxSalary(); |
1 | <!-- Map<String,Object> selectEmpNameAndMaxSalary(); --> |
这样返回的就是一个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 |
|
这时输出的大map,key为id,value为一个map,这个map里也包含了这个id;
这个大key要注意不可重复性;
模糊查询
都是拼接字符串实现,其中使用较多的是
1 | select * from xxx where xxx like "%"#{xxx}"%" |
用添加功能获取自增主键
例如功能,有两表clazz班级表和student学生表;要实现这样一个功能:
- 添加班级
- 获取新添加的班级id(id是自增主键,不是我们输入的)
- 为班级分配学生,也就是把学生的班级id修改为新添加的般的的id;
1 | void insertUser(User user); |
1 | <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> |
这里涉及到两个参数: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 | <!-- 专门声明一个resultMap设定column到property之间的对应关系 --> |
这样返回时返回的就是特定的resultMap, 对于结果,key就是指定的id,value就是上面的属性,最后返回的就是<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">
,这里所对应的类Employee;
关联关系
数量和方向
数量关系
主要体现在数据库表中
一对一
夫妻关系,人和身份证号
一对多
用户和用户的订单,锁和钥匙多对多
老师和学生,部门和员工方向
主要体现在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
表结构
结果例
也就是说,dept_name显然和Dept这个class是不对应的,要解决的就是如何将字段直接映射成对象属性的问题;
级联
必须使用resultMap处理;处理Emp类型的映射
1 | <resultMap id="empAndDeptResultMap" |
这样,结果就可以查询出来了(注意先重写toString方法)
切记,这里的resultMap是映射给Emp,给Emp内的特殊属性Dept,再调用内部的属性映射;
association
用于处理实体类类型的属性,包括一对一,多对一;
1 | <association property="dept" 指定sql查询出来的字段名 |
这就是association的配置
分步查询
首先,第一个sql
1 | <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap"> |
和常规的select一样,返回一个结果集,重点就在于这个结果集的配置,在这个sql里,查询到的是emp表的所有信息;
1 | <resultMap id="empAndDeptByStepResultMap" type="Emp"> |
在resultmap的association标签里,又引入了几个新属性:
1 | select="" column="" |
select绑定第二个sql语句,比如:
1 | Dpet getEmpAndDpetByStepTwo(@Param("deptId") |
关联俩个sql,在之前的sql-asscociation里:
1 | <association property="dept" |
总之,整个过程就是,第一条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属性;
延迟加载
比如查询到Customer的时候,不一定会使用Order的List集合数据。如果Order的集合数据始终没有使用,那么这部分数据占用的内存就浪费了。对此,我们希望不一定会被用到的数据,能够在需要使用的时候再去查询。
例如:对Customer进行1000次查询中,其中只有15次会用到Order的集合数据,那么就在需要使用时才去查询能够大幅度节约内存空间。
延迟加载的概念:对于实体类关联的属性到需要使用时才查询。也叫懒加载。
配置
1 | <!-- 使用settings对Mybatis全局进行设置 --> |
在高版本中,则
1 | <!-- Mybatis全局配置 --> |
那么在使用时,比如上面的emp和dept,如果我们查询了一个emp结果,但是在之后的的代码里没有对emp的dept这个属性操作,那么就只会执行第一条sql而不会执行第二条sql,反之,如果需要dept属性,则又会执行二条sql;总之就是先查询第一个sql本身,在需要内部某特殊属性时再调用第二条sql查询;
当然,也可以对某一条sql设置延迟加载而不使用全局配置,比如:fatchType=“eager/lazy”,在分布查询的连接association里使用这个属性来设置是否开启这组分布查询sql的延迟加载,默认遵循全局配置;
1 | <association property="dept" |
一对多
collection
类似association;区别在于collection处理一对多的关系,和association内部关注ofType属性而不是javaType属性
分步
第一个sql
第二个sql
组合
动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
If和Where
1 | <!-- List<Employee> selectEmployeeByCondition(Employee employee); --> |
重点,若包含满足的条件,where标签会生成where关键字并且自动去掉标签体内前面多余的and/or;
trim
使用trim标签控制条件部分两端是否包含某些字符
- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
1 | <!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) --> |
choose/when/otherwise
在多个分支条件中,仅执行一个。
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支,类似于if-else;
1 | <!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) --> |
set
实际开发时,对一个实体类对象进行更新。往往不是更新所有字段,而是更新一部分字段。此时页面上的表单往往不会给不修改的字段提供表单项。
1 | <form action="" method=""> |
例如上面的表单,如果服务器端接收表单时,使用的是User这个实体类,那么userName、userBalance、userGrade接收到的数据就是null。
如果不加判断,直接用User对象去更新数据库,在Mapper配置文件中又是每一个字段都更新,那就会把userName、userBalance、userGrade设置为null值,从而造成数据库表中对应数据被破坏。
此时需要我们在Mapper配置文件中,对update语句的set子句进行定制,此时就可以使用动态SQL的set标签。
1 | <!-- void updateEmployeeDynamic(Employee employee) --> |
foreach
1 | <!-- |
批量更新时需要注意
上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:
1 | atguigu.dev.url=jdbc:mysql://192.168.198.100:3306/mybatis-example?allowMultiQueries=true |
1 | <!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) --> |
SQL标签
抽取重复的SQL片段
1 | <!-- 使用sql标签抽取重复出现的SQL片段 --> |
1 | <!-- 使用include标签引用声明的SQL片段 --> |
使用
缓存
- 一级缓存:SqlSession级别
- 二级缓存:SqlSessionFactory级别
一级缓存
一级缓存失效的情况
- 不是同一个SqlSession
- 同一个SqlSession但是查询条件发生了变化
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
- 同一个SqlSession两次查询期间提交了事务
二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查间的结果会被缓存;
此后,若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled正”true”,默认为true,不需要设置
- 在映射文件中设置标签
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增册改,会使一级和二级缓存同时失效
二级缓存相关配置
在Mapper配置文件中添加的cache标签可以设置一些属性:
- eviction属性:缓存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。 - flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新 - size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出 - readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
查询顺序
- 二级缓存
- 一级缓存
- 数据库
*第三方缓存EhCache
导入Maven依赖
jar包名称 作用 mybatis-ehcache Mybatis和EHCache的整合包 ehcache EHCache核心包 slf4j-api SLF4J日志门面包 logback-classic 支持SLF4J门面接口的一个具体实现 创建EHCache配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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>指定缓存管理器的具体类型
还是到查询操作所在的Mapper配置文件中,找到之前设置的cache标签:
1
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
日志
存在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★ 优雅、强大 logback配置文件
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>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 | <!-- 依赖MyBatis核心包 --> |
MBG配置
文件名必须是:generatorConfig.xml
1 |
|
注意<jdbcConnection>
的修改和目标路径的配置 <javaModelGenerator targetPackage="com.atguigu.mybatis.entity" targetProject=".\src\main\java">
maven-执行MBG插件的generate目标
QBC查询
QBC查询最大的特点就是将SQL语句中的WHERE子句进行了组件化的封装,让我们可以通过调用Criteria对象的方法自由的拼装查询条件。
对于自动生成的条件查询,需要使用QBC查询去调用;
类似这样的方法,需要传入一个xxxExample的:
1 | List<Dept> selectByExample(DeptExample example); |
1 | <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.atguigu.mybatis.entity.DeptExample" > |
QBC查询例,也就是通过创建xxxExample的对象,通过这个对象再去创建Criteria对象,通过Criteria对象就可以给SQL语句添加条件;添加的条件andXxxxx可以一直追加;
那么对于or的连接,
也只需要再次掉用example对象,调用or方法后继续调用andXxx方法,即相当于xxx and xxx or xxx and xxx这样的形式;
1 | // 1.创建EmployeeExample对象 |
分页插件
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 | <dependency> |
在MyBatis的核心配置文件中配置插件
1 | <plugins> |
使用
依附于查询功能
在查询功能之前开启查询功能;
使用
PageHelper.startPage(int pageNum, int pageSize)
开启分页功能其中
pageNum:当前页的页码
pageSize:每页显示的条数
在查询获取list集合之后:
使用
PageInfo pageInfo = new PageInfo<>(List list, intnavigatePages)
获取分页相关数据ist:分页之后的数据
navigatePages:导航分页的页码数
PageHelper.startPage(int pageNum, int pageSize)
方法会返回一个page对象,这个对象包含page信息和内容信息;
PageInfo
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]