MyBatis-Plus
01. 概述
• MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
• 官网:https://mybatis.plus/ 或 https://mp.baomidou.com/
版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.0</version>
</dependency>
02. 快速入门
SpringBoot 整合 MyBatis-Plus,并实现根据Id查询功能。
2.1 数据库环境准备
创建一个数据库,导入部分数据
2.2 引入相关依赖
创建maven工程 springboot-mp(创建启动类、yml文件这里不再赘述),编写pom文件如下
<!-- 指定SpringBoot父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.darzen</groupId>
<artifactId>springboot-mp</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--mybatisplus启动器,传递依赖了JDBC启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!--mysql驱动,覆盖父工程的版本号-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 测试启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2.3 编写DataSource相关配置
resources目录下创建application.yml配置文件
# datasource
spring:
datasource:
url: jdbc:mysql://localhost:3306/mp # localhost:3306改成自己的数据库地址
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
2.4 编码
实体类
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 用户实体类
*/
@TableName("tb_user") // 指定表名
@Data
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
mapper数据访问层
import cn.itcast.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 使用mp定义Mapper,需要让Mapper接口继承 BaseMapper接口。
*/
public interface UserMapper extends BaseMapper<User> {
}
启动类增加 @MapperScan 注解
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*/
@MapperScan(basePackages = {"cn.darzen.dao"}) // 指定mapper包路径
@SpringBootApplication
public class MybatisPlusSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusSpringbootApplication.class, args);
}
}
2.5 测试
单元测试类
import cn.itcast.dao.UserMapper;
import cn.itcast.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 用户数据访问层单元测试类
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
/**
* 根据id查询用户
*/
@Test
public void testSelectById(){
User user = userMapper.selectById(1l);
System.out.println("user = " + user);
}
}
运行结果
user = User(id=1, userName=zhangsan, password=123456, name=张三, age=18, email=test1@itcast.cn)
03. 通用CRUD-新增
3.1 改造实体类
/**
* 实体类的属性名和数据库的字段名自动映射:
* 1.名称一样
* 2.数据库字段使用_分割,实体类属性名使用驼峰名称
*/
@TableName("tb_user")
@Data
public class User {
// 设置id生成策略:AUTO,数据库主键自增
@TableId(type = IdType.AUTO)
private Long id;
// @TableField("user-name")
private String userName;
private String password;
private String name;
private Integer age;
private String email;
// 不希望该值存入数据库
@TableField(exist = false)
private String info;
}
3.2 @TableField注解说明
1) @TableField("user_name") 指定映射关系
实体类的属性名和数据库的字段名自动映射:
* 名称一样
* 数据库字段使用_分割,实体类属性名使用驼峰名称
否则需要使用 @TableField("user_name") 指定映射关系
2) 忽略某个字段的查询和插入
@TableField(exist = false)
3) 设置id生成策略:AUTO 数据库主键自增
@TableId(type = IdType.AUTO)
3.3 属性配置说明
mybatis-plus:
global-config:
db-config:
# 表名前缀,设置后,如果数据库表名是tb_user, 那么实体类上可以不添加@TableName注解,不建议这里统一设置
table-prefix: tb_
# 全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置, 不建议这里统一设置
id-type: auto
configuration:
# 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.4 单元测试
测试用例
/**
* 新增用户
*/
@Test
public void testInsert(){
User user = new User();
user.setUserName("zhuge");
user.setName("诸葛孔明");
user.setPassword("123456");
user.setInfo("三国之卧龙"); // 不会保存到数据库,因为在实体类设置了@TableField(exist = false)
userMapper.insert(user);
}
运行结果
==> Preparing: INSERT INTO tb_user ( user_name, password, name ) VALUES ( ?, ?, ? )
==> Parameters: zhuge(String), 123456(String), 诸葛孔明(String)
<== Updates: 1
04. 通用CRUD-删除
4.1 根据id删除
测试用例
/**
* 根据id删除用户,删除id为6的数据
*/
@Test
public void testDeleteById(){
// 返回值代表删除成功的记录数
int count = userMapper.deleteById(6l);
}
运行结果
==> Preparing: DELETE FROM tb_user WHERE id=?
==> Parameters: 6(Long)
<== Updates: 1
4.2 根据id集合批量删除
测试用例
/**
* 根据ID集合批量删除,删除id为4、5的数据
*/
@Test
public void testDeleteByBatchIds(){
List<Long> ids = new ArrayList();
ids.add(4l);
ids.add(5l);
// 返回值代表删除成功的记录数
int count = userMapper.deleteBatchIds(ids);
}
运行结果
==> Preparing: DELETE FROM tb_user WHERE id IN ( ? , ? )
==> Parameters: 4(Long), 5(Long)
<== Updates: 2
4.3 根据map条件删除
测试用例
/**
* 根据Map条件删除,删除user_name为zhangsan 且 年龄为18的数据
*/
@Test
public void testDeleteByMap(){
Map<String, Object> params = new HashMap<>();
// key-代表数据库字段名, value-匹配的值
// 注意:map中如果put多个判断条件,默认以and连接
params.put("user_name", "zhangsan"); // 相当于where user_name = 'zhangsan'
params.put("age", 18);
// count代表删除成功的记录数
int count = userMapper.deleteByMap(params);
}
运行结果
==> Preparing: DELETE FROM tb_user WHERE user_name = ? AND age = ?
==> Parameters: zhangsan(String), 18(Integer)
<== Updates: 1
4.4 根据条件构造器删除
问题:如果条件比较复杂,where条件有like、in、or等条件呢?就可以根据条件构造器QueryWrapper删除了
单元测试
/**
* 根据条件构造器删除
*/
@Test
public void testDeleteByWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 参数1-字段名,参数2-匹配的值
wrapper.eq("user_name", "lisi");
// count代表删除成功的记录数
int count = userMapper.delete(wrapper);
}
运行结果
==> Preparing: DELETE FROM tb_user WHERE (user_name = ?)
==> Parameters: lisi(String)
<== Updates: 1
05. 通用CRUD-更新
5.1 根据id更新
由于之前的数据不多了,操作之前可以先把数据库表删了,再重新执行以下sql脚本,或者自己手动添加新数据亦可。
测试用例
/**
* 根据id更新,将id为1的数据,年龄改为100
*/
@Test
public void testUpdateById(){
User user = new User();
user.setId(1l); // 必须设置,因为是根据id更新
user.setAge(100); // 修改后的值
// 修改成功的记录数
int count = userMapper.updateById(user);
}
运行结果
==> Preparing: UPDATE tb_user SET age=? WHERE id=?
==> Parameters: 100(Integer), 1(Long)
<== Updates: 1
5.2 根据条件构造器更新
测试用例
/**
* 根据条件构造器更新, 将user_name为zhangsan的name改为司马懿,age改为60
*/
@Test
public void testUpdateByWrapper() {
// user对象为修改后的值
User user = new User();
user.setName("司马懿");
user.setAge(60);
// where条件
UpdateWrapper<User> updateWrapper = new UpdateWrapper();
updateWrapper.eq("user_name", "zhangsan");
int count = userMapper.update(user, updateWrapper);
}
运行结果
==> Preparing: UPDATE tb_user SET name=?, age=? WHERE (user_name = ?)
==> Parameters: 司马懿(String), 60(Integer), zhangsan(String)
<== Updates: 1
06. 通用CRUD-查询
查询相关API非常多,有根据ID查询、根据ID批量查询、根据条件构造器查询、根据Map条件查询,分页查询、查询总记录数等等…
6.1 基本比较查询
语法
eq( ) : 等于 =
ne( ) : 不等于 <>
gt( ) : 大于 >
ge( ) : 大于等于 >=
lt( ) : 小于 <
le( ) : 小于等于 <=
between ( ) : BETWEEN 值1 AND 值2
notBetween ( ) : NOT BETWEEN 值1 AND 值2
in( ) : in
notIn( ) : not in
测试用例
/**
* 基本比较查询: where user_name = 'lisi' and age < 23 and name in ('李四', '王五')
*/
@Test
public void testWrapper1(){
// 创建查询条件构造器
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 设置查询条件,多个条件之间默认以and连接
queryWrapper.eq("user_name", "lisi")
.lt("age", 23)
.in("name", "李四", "王五");
// select * from tb_user where user_name = 'lisi' and age < 23 and name in ('李四', '王五')
List<User> userList = userMapper.selectList(queryWrapper);
for (User user : userList) {
System.out.println("user = " + user);
}
}
运行结果
==> Preparing: SELECT id,user_name,password,name,age,email FROM tb_user WHERE (user_name = ? AND age < ? AND name IN (?,?))
==> Parameters: lisi(String), 23(Integer), 李四(String), 王五(String)
<== Columns: id, user_name, password, name, age, email
<== Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7abe27bf]
user = User(id=2, userName=lisi, password=123456, name=李四, age=20, email=test2@itcast.cn, info=null)
6.2 逻辑查询
语法
and 参数为Consumer lamboda表达式,用于隔离多组条件
or 后面接其他条件(基本比较查询的条件)
测试用例一
/**
* 逻辑查询-or:查询 user_name = lisi 或 age > 10
*/
@Test
public void testLogicOr(){
// 创建查询条件构造器
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 设置查询条件
queryWrapper.eq("user_name", "lisi")
.or() // 不加or默认是以and连接多个条件
.gt("age", 10);
// select * from tb_user where user_name = 'lisi' or age > 10
List<User> userList = userMapper.selectList(queryWrapper);
for (User user : userList) {
System.out.println("user = " + user);
}
}
运行结果一
==> Preparing: SELECT id,user_name,password,name,age,email FROM tb_user WHERE (user_name = ? OR age > ?)
==> Parameters: lisi(String), 10(Integer)
<== Columns: id, user_name, password, name, age, email
<== Row: 1, zhangsan, 123456, 司马懿, 60, test1@itcast.cn
<== Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
<== Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
<== Row: 4, zhaoliu, 123456, 赵六, 21, test4@itcast.cn
<== Row: 5, sunqi, 123456, 孙七, 24, test5@itcast.cn
<== Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@796065aa]
user = User(id=1, userName=zhangsan, password=123456, name=司马懿, age=60, email=test1@itcast.cn, info=null)
user = User(id=2, userName=lisi, password=123456, name=李四, age=20, email=test2@itcast.cn, info=null)
user = User(id=3, userName=wangwu, password=123456, name=王五, age=28, email=test3@itcast.cn, info=null)
user = User(id=4, userName=zhaoliu, password=123456, name=赵六, age=21, email=test4@itcast.cn, info=null)
user = User(id=5, userName=sunqi, password=123456, name=孙七, age=24, email=test5@itcast.cn, info=null)
测试用例二
/**
* 逻辑查询-and:查询 age > 23 and (name=王五 or name = 李四)
*/
@Test
public void testLogicAnd(){
// 创建查询条件构造器
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 设置查询条件
queryWrapper.gt("age", 23)
// 相当于and (), ()中可指定多个条件
.and(i -> i.eq("name", "王五").or().eq("name", "李四"));
// select * from tb_user where user_name = 'lisi' and (name = '王五' or name = '李四')
List<User> userList = userMapper.selectList(queryWrapper);
for (User user : userList) {
System.out.println("user = " + user);
}
}
运行结果二
==> Preparing: SELECT id,user_name,password,name,age,email FROM tb_user WHERE (age > ? AND (name = ? OR name = ?))
==> Parameters: 23(Integer), 王五(String), 李四(String)
<== Columns: id, user_name, password, name, age, email
<== Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@47f08b81]
User(id=3, userName=wangwu, password=123456, name=王五, age=28, email=test3@itcast.cn, info=null)
6.3 模糊查询
语法
like 相当于 like '%关键字%'
notLike 相当于 not like '%关键字%'
likeLeft 相当于 like '%关键字'
likeRight 相当于 like '关键字%'
测试用例
/**
* 模糊查询:user_name like 'zhang%'
*/
@Test
public void testLike(){
// 1.创建查询条件构建器
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 2.设置条件
wrapper.likeRight("user_name", "zhang");
// SELECT * from tb_user where user_name like 'zhang%'
List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println("user = " + user);
}
}
运行结果
==> Preparing: SELECT id,user_name,password,name,age,email FROM tb_user WHERE (user_name LIKE ?)
==> Parameters: zhang%(String)
<== Columns: id, user_name, password, name, age, email
<== Row: 1, zhangsan, 123456, 司马懿, 60, test1@itcast.cn
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3d36dff4]
user = User(id=1, userName=zhangsan, password=123456, name=司马懿, age=60, email=test1@itcast.cn, info=null)
6.4 排序查询
语法
orderBy : 指定排序规则
orderByAsc : 升序排序
orderByDesc : 降序排序
测试用例
/**
* 排序:查询年龄小于50并且根据年龄升序排序
*/
@Test
public void testOrder() {
// 1.创建查询条件构建器
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 2.设置条件
wrapper.lt("age", 50)
// 参数1:是否要作为排序条件(肯定为true啦),参数2-是否升序(true为升序),参数3-排序字段名
.orderBy(true, true, "age");
// select * from tb_user where age < 50 order by age asc
List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println("user = " + user);
}
}
运行结果
==> Preparing: SELECT id,user_name,password,name,age,email FROM tb_user WHERE (age < ?) ORDER BY age ASC
==> Parameters: 50(Integer)
<== Columns: id, user_name, password, name, age, email
<== Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
<== Row: 4, zhaoliu, 123456, 赵六, 21, test4@itcast.cn
<== Row: 5, sunqi, 123456, 孙七, 24, test5@itcast.cn
<== Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
<== Total: 4
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7abe27bf]
user = User(id=2, userName=lisi, password=123456, name=李四, age=20, email=test2@itcast.cn, info=null)
user = User(id=4, userName=zhaoliu, password=123456, name=赵六, age=21, email=test4@itcast.cn, info=null)
user = User(id=5, userName=sunqi, password=123456, name=孙七, age=24, email=test5@itcast.cn, info=null)
user = User(id=3, userName=wangwu, password=123456, name=王五, age=28, email=test3@itcast.cn, info=null)
6.5 select
问题:查询相关API默认是返回所有字段,相当于select * from 表名,若想要指定返回的字段就可以使用select API。
/**
* 测试select字段
*/
@Test
public void testSelect(){
// 1.创建查询条件构建器
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 2.设置条件
wrapper.lt("age", 50)
.orderBy(true, true, "age")
.select("user_name", "age"); // 指定要返回的字段
List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user);
}
}
运行结果
==> Preparing: SELECT user_name,age FROM tb_user WHERE (age < ?) ORDER BY age ASC
==> Parameters: 50(Integer)
<== Columns: user_name, age
<== Row: lisi, 20
<== Row: zhaoliu, 21
<== Row: sunqi, 24
<== Row: wangwu, 28
<== Total: 4
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7abe27bf]
user = User(id=null, userName=lisi, password=null, name=null, age=20, email=null, info=null)
user = User(id=null, userName=zhaoliu, password=null, name=null, age=21, email=null, info=null)
user = User(id=null, userName=sunqi, password=null, name=null, age=24, email=null, info=null)
user = User(id=null, userName=wangwu, password=null, name=null, age=28, email=null, info=null)
从运行结果可以看出,只返回user_name和age字段
6.6 分页查询
编写配置类,添加拦截器
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PageConfig {
/**
* 3.4.0之前的版本用这个
* @return
*/
/*
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
*/
/**
* 3.4.0之后提供的拦截器的配置方式
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
编写单元测试类
/**
* 分页查询
*/
@Test
public void testPage(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lt("age", 50)
.orderBy(true, true, "age")
.select("user_name", "age"); // 指定要返回的字段
// 参数1-当前页,参数2-每页显示数
Page<User> page = new Page<User>(1, 2); // 查询第一页,每页显示2条
Page<User> result = userMapper.selectPage(page, wrapper);
List<User> records = result.getRecords(); // 用户数据
System.out.println("总条数 = " + result.getTotal());
System.out.println("总页数 = " + result.getPages());
for (User user : records) {
System.out.println(user);
}
}
运行结果
==> Preparing: SELECT COUNT(1) FROM tb_user WHERE (age < ?)
==> Parameters: 50(Integer)
<== Columns: COUNT(1)
<== Row: 4
<== Total: 1
==> Preparing: SELECT user_name,age FROM tb_user WHERE (age < ?) ORDER BY age ASC LIMIT ?
==> Parameters: 50(Integer), 2(Long)
<== Columns: user_name, age
<== Row: lisi, 20
<== Row: zhaoliu, 21
<== Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@383f1975]
总条数 = 4
总页数 = 2
User(id=null, userName=lisi, password=null, name=null, age=20, email=null, info=null)
User(id=null, userName=zhaoliu, password=null, name=null, age=21, email=null, info=null)
07. 使用原生mybatis
**问题:**上面讲解的API 都是Mybatis Plus对于单表CRUD进行的封装,那在实际开发中,可能存在复杂sql的编写,例如:多表联查,子查询等等,此类操作就需要开发人员使用原生Mybatis的功能,自己写sql语句了。
7.1 基本使用
第一步:yml文件中配置相关属性
mybatis-plus:
global-config:
db-config:
# 全局设置自增,设置后,实体类中不需要通过@TableId()
id-type: auto
table-prefix: tb_
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印sql
# 设置sql返回字段驼峰转换
map-underscore-to-camel-case: true
# 指定xml文件路径
mapper-locations:
- classpath:/mappers/*.xml
# 返回值别名包路径
type-aliases-package: cn.darzen.pojo
第二步:resoures目录下创建mappers文件夹,然后在mappers文件夹下创建UserMapper.xml文件(可使用插件J2EECfgFile插件)
第三步:编写UserMapper.xml
<?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">
<!--指定userMapper接口的路径-->
<mapper namespace="cn.darzen.dao.UserMapper">
<select id="findById" resultType="User">
select * from tb_user where id = #{id}
</select>
</mapper>
第四步:编写UserMapper.java接口
import cn.darzen.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 使用mp定义Mapper,需要让Mapper接口继承 BaseMapper接口。
*/
public interface UserMapper extends BaseMapper<User> {
/**
* 根据id查询用户
* @param id
* @return
*/
public User findById(Long id);
}
第五步:编写单元测试用例
/**
* 测试自定义sql
*/
@Test
public void testFindById(){
User user = userMapper.findById(1l);
System.out.println("user = " + user);
}
运行结果
==> Preparing: select * from tb_user where id = ?
==> Parameters: 1(Long)
<== Columns: id, user_name, password, name, age, email
<== Row: 1, zhangsan, 123456, 司马懿, 60, test1@itcast.cn
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@40368a46]
user = User(id=1, userName=zhangsan, password=123456, name=司马懿, age=60, email=test1@itcast.cn, info=null)
7.2 分页查询
问题:使用原生的mybatis功能,如何加入Mybatis Plus的分页?
第一步:编写UserMapper.xml
<?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">
<!--指定userMapper接口的路径-->
<mapper namespace="cn.itcast.dao.UserMapper">
<select id="findById" resultType="User">
select * from tb_user where id = #{id}
</select>
<!--分页查询-->
<select id="selectByPage" resultType="User">
select * from tb_user where age > #{lowerNum}
</select>
</mapper>
第二步:编写UserMapper.java接口,添加方法
/**
* 分页查询
* @param page
* @param lowerNum:最小值
* @return
*/
public Page<User> selectByPage(Page page, @Param("lowerNum") int lowerNum);
第三步:编写单元测试用例
/**
* 测试自定义分页查询
*/
@Test
public void testSelectByPage(){
// 定义分页对象
Page<User> page = new Page<>(1, 2);
page = userMapper.selectAllByPage(page, 0);
List<User> records = page.getRecords(); // 返回用户数据
long total = page.getTotal(); // 总条数
long pages = page.getPages(); // 总页数
for (User user : records) {
System.out.println("user = " + user);
}
System.out.println("总条数 = " + total);
System.out.println("总页数 = " +pages);
}
运行结果
==> Preparing: SELECT COUNT(1) FROM tb_user WHERE age > ?
==> Parameters: 0(Integer)
<== Columns: COUNT(1)
<== Row: 5
<== Total: 1
==> Preparing: select * from tb_user where age > ? LIMIT ?
==> Parameters: 0(Integer), 2(Long)
<== Columns: id, user_name, password, name, age, email
<== Row: 1, zhangsan, 123456, 张三, 18, test1@itcast.cn
<== Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
<== Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@476a736d]
user = User(id=1, userName=zhangsan, password=123456, name=张三, age=18, email=test1@itcast.cn, info=null)
user = User(id=2, userName=lisi, password=123456, name=李四, age=20, email=test2@itcast.cn, info=null)
总条数 = 5
总页数 = 3
08. 通用枚举(了解)
1)为什么使用通用枚举?
目前的实体类这样设计
@Data
@TableName("tb_user") // 指定表名
public class User {
private Long id;
private String userName;
// 性别 1: 男 2: 女
private Integer sex;
}
那么最终获取到的JSON数据应该类似于这样:
{
"id":100,
"userName":"jack",
"sex":1
}
如若使用MyBatis-Plus的枚举自动关联注入,可以更优雅的实现如下效果
{
"id":100,
"userName":"jack",
"sex":"男"
}
2)实现步骤
第一步:修改表结构
ALTER TABLE `tb_user` ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女';
第二步:定义枚举类
import com.baomidou.mybatisplus.annotation.IEnum;
/**
* 性别枚举类
*/
public enum SexEnum implements IEnum<Integer> {
MAN(1, "男"),
WOMEN(2, "女");
private int value;
private String desc;
SexEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
@Override
public String toString() {
return this.desc;
}
}
第三步:修改yml配置,扫描枚举类所在包
mybatis-plus:
global-config:
db-config:
# 全局设置自增,设置后,实体类中不需要通过@TableId()
id-type: auto
table-prefix: tb_
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印sql
# 设置sql返回字段驼峰转换
map-underscore-to-camel-case: true
# 指定xml文件路径
mapper-locations:
- classpath:/mappers/*.xml
# 返回值别名包路径
type-aliases-package: cn.darzen.pojo
# 指定枚举类包路径
type-enums-package: cn.darzen.enums
第四步:修改实体类,增加性别字段
@TableField(value = "sex")
private SexEnum sex;
3)测试
查询测试
/**
* 测试通用枚举-查询
*/
@Test
public void testEnum1(){
// 参数为null代表无查询条件
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println("user = " + user);
}
}
数据库数据:
运行结果:
从上图可以看出,查询时自动将数据库的数据由 1 转为了 男(会使用枚举类的重写的toString方法返回值)。
新增测试
/**
* 测试通用枚举-新增
*/
@Test
public void testEnum2(){
User user = new User();
user.setName("赵子龙");
user.setAge(18);
user.setUserName("zhaozilong");
user.setPassword("123456");
user.setEmail("1111@sina.com");
user.setSex(SexEnum.MAN); // 使用枚举值
userMapper.insert(user);
}
数据库数据:
运行结果:
上图可以看出,当插入数据时,会将枚举值转换为1,也就是枚举类中的value属性值
09. service 封装(了解)
Mybatis-Plus 为了开发更加快捷,对业务层也进行了封装,直接提供了相关的接口和实现类。我们在进行业务层开发时,可以继承它提供的接口和实现类,使得编码更加高效。
步骤
-
定义接口继承IService
-
定义实现类继承ServiceImpl<Mapper,Entity> 实现定义的接口
定义Service接口
import cn.itcast.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
}
定义Service实现类
import cn.darzen.dao.UserMapper;
import cn.darzen.pojo.User;
import cn.darzen.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
/**
* test
*/
public void test(){
// this.baseMapper可以获取到userMapper对象,相当于已经注入进来了并且放到了this.baseMapper中;
// 说明:Iservice中也包含了一些封装的CRUD方法
UserMapper baseMapper = this.baseMapper;
}
}
调用Service示例
项目结构:
10. 自动填充(了解)
介绍
我们在进行数据库表的更新/插入操作时,如果有创建时间、更新时间,每次插入都需要手动设置字段值,这就很麻烦。而Mybatis Plus提供了自动填充字段的设置。即给某些指定字段设置默认值。
代码实现
1)修改pojo(数据库表中,自己创建created和updated字段,DateTime类型)
@Data
public class User {
...
// 创建时间,插入一条user数据时候,会自动设置创建时间到updated中
@TableField(fill = FieldFill.INSERT)
private Date created;
// 修改时间,执行修改数据时候,会自动设置修改时间到updated中
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated;
}
2)编写handler
package cn.darzen.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 参数1-字段名,参数2-默认值,参数3-固定写
this.setFieldValByName("created", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
// 参数1-字段名,参数2-默认值,参数3-固定写
this.setFieldValByName("updated", new Date(), metaObject);
}
}
3)测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class Test2 {
@Autowired
private UserService userService;
@Test
public void save() {
User user = new User();
user.setUserName("老王2");
user.setAge(20);
userService.save(user);
}
@Test
public void update() {
User user = new User();
user.setId(1350766462467121172L);
user.setUserName("老王2");
user.setAge(10);
userService.updateById(user);
}
}
11. 代码生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
11.1 创建maven工程
创建maven工程:mybatisplus_code
11.2 导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--父工程给生成后的代码用的-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>cn.darzen</groupId>
<artifactId>mybatisplus_code</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<!--模块引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!--下面是生成后的代码需要用到的依赖-->
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
11.3 代码生成类
创建test包,复制此类到包下
package test;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Scanner;
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir"); // 获取当前项目所在目录
System.out.println(projectPath);
// 复制自己工程的绝对路径作为代码输出目录
gc.setOutputDir("D:\\itcast\\ee148\\project148\\mybatisplus_code\\src\\main\\java");
gc.setAuthor("itheima"); // 设置作者
//设置包
/* gc.setControllerName("Usercontroller");
gc.setServiceName("UserService");
gc.setEntityName("User");
gc.setServiceImplName("UserServiceImpl");
gc.setMapperName("UserDao"); */
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
// 设置全局配置
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mp");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc); // 设置数据源
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.itheima"); // 包前缀
pc.setModuleName(scanner("功能模块名")); // 设置功能模块名
// 设置包配置
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
// strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix("tb_"); // 设置表前缀
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
// 执行
mpg.execute();
}
}
11.4 执行
右键运行main函数,在控制台输入功能模块名,表名,再按回车即可
执行完成后刷新项目,发现代码已经生成完毕,每个表都生成对应每一层的代码: