MyBatisPlus总结
Posted 小白菜00
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatisPlus总结相关的知识,希望对你有一定的参考价值。
目录
MyBatisPlus
含义:mybatis-plus是个mybatis的增强工具,在mybatis的基础上只做增强不做改变,为简化开发,提高效率而生
注意:我们可以直接在mybatis的基础上直接去集成mybatisplus,这样并不会影响mybatis的功能,同时我们也可以使用他所提供的功能。
MP特点
- 对mybatis只做增强不做改变,引入他不会对现有工程产生影响
- 只需要简单的配置即可快速进行单表CRUD操作,从而节省大量的时间
- 代码生成,自动分页,逻辑删除,自动填充功能等一应俱全
MP框架结构
理解:
- MP由MP启动器、注解部分、扩展部分、core核心部分、代码生成部分所组成;由这些内容共同支持了MP去实现功能的过程
- MP实现功能:首先,扫描实体类,扫描之后通过反射技术将实例类中的属性进行抽取,抽取之后来分析表和实体类之间的关系;以及通过反射所抽取出来的实体类中的属性与我们当前字段之间的关系;再根据我们当前调用的方法来生成相对应的sql语句,然后再把增删改查的sql语句注入到mybatis的容器中从而实现最终的功能
MP使用准备
导入依赖
<dependencies>
<!--springboot启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--springboot测试-->
<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>
<optional>true</optional>
</dependency>
<!--MP启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--mysql驱动包--><!--测试功能的启动器-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
springboot整合mybatisplus配置文件
spring:
#设置数据源信息
datasource:
#配置数据源类型
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///mybatis_plus?characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8
username: root
password: root
mybatis-plus:
configuration:
#MP提供了日志功能
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#驼峰映射(默认就是开启的)
map-underscore-to-camel-case: true
#设置MP的全局配置
global-config:
db-config:
#这样设置的话,那么实体类所有的表都会加上t_前缀
table-prefix: t_
#设置统一主键生成策略
id-type: auto
#映射文件路径
mapper-locations: classpath:/mapper/UserMapper.xml
#配置类型别名所对应的包
type-aliases-package: cn.tedu.mybatisplus.pojo
#扫描通用枚举的包
type-enums-package: cn.tedu.mybatisplus.enums
定义好实体类User后编辑mapper接口
//使用MP提供的通用mapper——BaseMapper
//BaseMapper里的泛型表示实体类的类型
@Mapper
public interface UserMapper extends BaseMapper<User>
注意:
- MP封装了许多常用的CRUD方法,用户需要的时候只需要继承公共的业务接口BaseMapper即可,进而提高了效率
- BaseMapper接口必须添加泛型,因为其要根据泛型对象获得指定的注解(并获取其中注解的值)、表名及其其中的属性
@Mapper与@MapperScan("包名")区别
- @MapperScan("包名"):扫描指定包下的mapper接口,将该接口的代理类交给spring容器来保存
- @Mapper:将该接口的代理类交给spring容器来保存
- @Mapper用在指定接口上,@MapperScan("对应接口所在包名")用在启动类或配置文件上
MP基本操作
注意:使用前注入userMapper
新增操作
User user = new User();
user.setName("lili").setAge(23).setEmail("lili@qq.com");
int insert = userMapper.insert(user);
System.out.println(insert);
//mybatis-plus会自动获取id
System.out.println(user.getId());
删除操作
通过id删除用户
int i = userMapper.deleteById(7);
System.out.println(i);
通过map作为条件删除
Map<String,Object> map=new HashMap<>();
map.put("name", "张三");
map.put("age", 23);
//删除name为张三,age为23的人
int i = userMapper.deleteByMap(map);
System.out.println(i);
通过多个id实现删除
List<Long> list = Arrays.asList(1L, 2L, 3L);
int i = userMapper.deleteBatchIds(list);
System.out.println(i);
更新用户
通过id进行用户更新
User user = new User();
user.setId(3L).setName("lan").setEmail("lan@qq.com");
//根据id修改元素
int i = userMapper.updateById(user);
System.out.println(i);
查询用户
根据id查询用户
User user = userMapper.selectById(1L);
System.out.println(user);
根据多个id查询用户
List<Long> list = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.selectBatchIds(list);
users.forEach(System.out::println);
根据map集合作为条件查询用户
HashMap<String, Object> map = new HashMap<>();
map.put("name", "lan");
map.put("age", 28);
List<User> users = userMapper.selectByMap(map);
//list会直接打印对象数组
System.out.println(users);
通用Service接口
说明:通用Service封装了IService接口,进一步封装了CRUD采用get查询单行,remove删除,list查询集合,page分页等前缀命名方式,区分Mapper层,避免混淆
//service接口
public interface UserService extends IService<User>
//service实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService
注意:
- ServiceImpl<UserMapper, User>实现了IService<User>接口
- 使用时应先注入userService
一些操作
查询总记录数
//查询总记录数
long count = userService.count();
System.out.println(count);
批量添加数据
ArrayList<User> list = new ArrayList<>();
for (int i = 1; i <=10 ; i++)
User user = new User();
user.setName("cjc"+i).setAge(10+i);
list.add(user);
//批量添加数据
boolean b = userService.saveBatch(list);
//操作成功或失败
System.out.println(b);
MP常用注解
//设置实体类所对应的表名,若对象与表名一致,则表名中()可以省略
@TableName("t_user")
public class User
//将当前属性对应的字段指定为主键(将该属性与数据库中的id字段进行映射),并通过雪花算法生成主键id
//type标识主键的生成策略为自动递增,要求数据库的主键为自增策略(默认为雪花算法——IdType.ASSIGN.ID)
@TableId(value = "id")
private Long id;
//将该注解标识的属性与数据库中的name字段一一映射,若属性名与字段名相同,则注解可省略
@TableField(value = "name")
private String name;
private Integer age;
private String email;
//逻辑删除0标识未删除,1标识已删除
//被逻辑删除的数据用户查不到,但是可以在数据库中看到,只是该属性变为1;(为修改操作)
@TableLogic
private Integer isDeleted;
注意:@TableField(exit=false)注解一般用在注入的属性上,被该注解标识表名当前属性不参与MP的操作
雪花算法
前言
背景:需要合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量
数据库表的扩展方式:业务分库、主从复制、数据库分表
数据库分表的两种方式
- 垂直分表
- 水平分表
垂直分表
将重要的数据放到一个表中,不在业务查询中用到的独立到另一个表中,以提升一定的性能
水平分表
主键自增:比如按照范围分表(1-9999放入表一,10000-20000放入表二)
取模:主键%数据库个数,余数相同的放入一个表中
雪花算法:
雪花算法是由Twitter分布式主键生成算法,他保证不同表的主键的不重复性,以及相同表的主键的有序性
核心思想:
- 长度为64bit
- 首先是符号位,1bit标识,由于long基本类型在java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
- 4bit是时间戳(ms级别),存储的是时间戳的差值(当前时间戳-开始时间戳),结果约为69.73年
- 10bit作为机器的id(5bit是数据中心,5bit是机器id,可以部署在1024个节点上)
- 12bit作为ms内的流水号(意味着每个节点在每毫秒可以产生4096个id)
优点:整体上按照时间自增排序,并且整个分布式系统内不会产生id碰撞
条件构造器
作用:封装当前的条件
继承结构
AbstractWrapper:用于条件查询封装,生成sql的where条件
- QueryWrapper:查询条件封装
- UpdateWrapper:Update条件封装
- AbstractLambdaWrapper:使用lambda语法
AbstractLambdaWrapper
- LambdaQueryWrapper:用于lambda语法使用的查询Wrapper
- LambdaUpdateWrapper:lambda更新封装Wrapper
使用条件构造器实现查询操作
查询所有用户
//通过条件构造器查询一个list集合,若没有条件则可以设置null(相当于查询所有)
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
根据构造器查询主键字段集合
//查询name为lei的主键字段
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "lei");
List<Object> list = userMapper.selectObjs(queryWrapper);
System.out.println(list);
根据条件构造器查询多用户
//查询用户名包含a,年龄在20-30之间,邮箱信息不为null的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper可以实现链式加载
queryWrapper.like("name", "a").between("age", 20, 30).isNotNull("email");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
//对象形式
User user = new User();
user.setAge(28);
QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
//user内的属性最终会以and形式拼接
关于模糊查询
- like:表示a左右都有%
- likeleft:表示a左边有%
- likeright:表示a右边有%
in查询
//in查询,查询id为1,2,3的数据
Integer[] ids=1,2,3;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.in("id",ids);
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
转义字符方式查询并排序
//查询id>2的用户,按照年龄降序排序,若年两相同则按照id升序排序
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("id", 2).orderByDesc("age").orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
转义字符
- >:gt
- <:lt
- =:eq
- >=:ge
- <=:le
- != :ne
条件构造器实现删除操作
//删除邮箱地址为null的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int i = userMapper.delete(queryWrapper);
System.out.println(i);
使用条件构造器实现修改操作
//将年龄>20并且用户名中包含a或邮箱为null的用户进行修改(默认情况下就是and连接)
//修改条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20).like("name", "a").or().isNull("email");
User user = new User();
user.setName("lei").setEmail("test@cj.com");
int i = userMapper.update(user, queryWrapper);
System.out.println(i);
条件的优先级
//将用户名中包含a并且(年龄大于20或邮箱为null)的用户信息进行修改
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//lambda中的条件优先执行(i就表示条件构造器)
queryWrapper.like("name", "a").and(i-> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setName("red").setEmail("test@cj.com");
int i = userMapper.update(user, queryWrapper);
System.out.println(i);
组装select语句
//查询出来一个以map为泛型的list集合
//查询用户名、年龄、邮箱信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name","age","email");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
System.out.println(maps);
组装子查询
//select * from t_user where id in(select id from t_user where id<=100)
//查询id<=100的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from t_user where id<=100");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
动态sql查询
String name=null;
String age="21";
//判断字符串是否为null或空串若为返回false,不为返回true
boolean pn = StringUtils.hasLength(name);
boolean pa = StringUtils.hasLength(age);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//判断属性是否为true,为true则执行该条件,不为则忽略该条件
queryWrapper.eq(pn,"name",name).eq(pa, "age", age);
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
注意:queryWrapper.clear();为清除多余的条件,清除后queryWrapper可以继续使用
使用updateWrapper实现修改功能
//查询用户名中包含a(年龄>20或邮箱为null)的员工信息
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//修改条件
updateWrapper.like("name", "a").and(i->i.gt("age", 20).isNull("email"));
//修改内容
updateWrapper.set("name", "lala").set("email", "www@cjc.com");
int i = userMapper.update(null, updateWrapper);
System.out.println(i);
LambdaQueryWrapper
作用:防止我们太笨,而把字段名写错进而提供了一个函数式接口来访问我们实体类中的某一个属性,当我们把属性访问之后,那么他就可以自动的获取属性所对应的字段名,来当作作为条件的哪个字段
String name="a";
Integer ageBegin=null;
Integer ageEnd=30;
//主要避免了名称写错进而提供了直接访问表达式::
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(StringUtils.isNotBlank(name), User::getName,name)
.ge(ageBegin!=null, User::getAge,ageBegin)
.le(ageEnd!=null, User::getAge,ageEnd);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
System.out.println(users);
LambdaUpdateWrapper
//查询用户名中包含a(年龄>20或邮箱为null)的员工信息
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
//修改条件
updateWrapper.like(User::getName, "a").and(i->i.gt(User::getAge, 20).isNull(User::getEmail));
//修改内容
updateWrapper.set(User::getName, "lala").set(User::getEmail, "www@cjc.com");
int i = userMapper.update(null, updateWrapper);
System.out.println(i);
MP的分页插件
配置配置类(必然)
@Configuration
public class MPConfig
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor()
//创建mybatisplus拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//向拦截器中添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
MP实现分页
//测试类内
//两个参数——当前页页码,每页信息条数
Page<User> page = new Page<>(2,3);
//两个参数——分页对象,条件构造器
userMapper.selectPage(page, null);//因为我对所有的查询所以条件构造器为null——返回值还为page
//获取当前页数据
List<User> records = page.getRecords();
System.out.println(records);
//获取总记录数
long total = page.getTotal();
System.out.println(total);
//获取总页数
long pages = page.getPages();
System.out.println(pages);
//是否有下一页
System.out.println(page.hasNext());
//是否有上一页
System.out.println(page.hasPrevious());
自定义分页功能
//自定义接口:
//mybatisplus提供的分页对象,必须为于第一个参数的位置
Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
//自定义配置文件sql
<select id="selectPageVo" resultType="User">
select id,name,age,email from t_user where age>#age
</select>
//测试类
Page<User> page = new Page<>(2, 2);
userMapper.selectPageVo(page,20);
//获取当前页数据
List<User> records = page.getRecords();
System.out.println(records);
//获取总记录数
long total = page.getTotal();
System.out.println(total);
//获取总页数
long pages = page.getPages();
System.out.println(pages);
//是否有下一页
System.out.println(page.hasNext());
//是否有上一页
System.out.println(page.hasPrevious());
MP乐观锁
在需要改变的实体类中加version注解
@Data
public class Product
private Long id;
private String name;
private Integer price;
@Version//用来标识乐观锁版本号字段
private Integer version;
添加乐观锁插件
@Configuration
public class MPConfig
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor()
//创建mybatisplus拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//向拦截器中添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
测试
注意:下面小王和小李拿的是同一个数据
//小李查询商品价格
Product productLi = productMapper.selectById(1);
System.out.println("小李"+productLi.getPrice());
//小王查询商品价格
Product productWang = productMapper.selectById(1);
System.out.println("小王"+productWang.getPrice());
//小李将商品价格+50
productLi.setPrice(productLi.getPrice()+50);
productMapper.updateById(productLi);
//小王将商品价格-30
productWang.setPrice(productWang.getPrice()-30);
int result = productMapper.updateById(productWang);
if (result==0)
//操作失败后重试
Product productNew = productMapper.selectById(1);
productNew.setPrice(productNew.getPrice()-30);
productMapper.updateById(productNew);
通用枚举
实体类中有枚举类型,那么怎么将该枚举类型存入到数据库中呢
//为该枚举添加注解
@Getter
public enum SexEnum
MALE(1,"男"),
FEMALE(2,"女");
@EnumValue//将注解所标识的属性的值存储到数据库中(因为数据库中存放的是数字)
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName)
this.sex = sex;
this.sexName = sexName;
配置通用枚举扫描包
mybatis-plus.type-enums-package=cn.tedu.mybatisplus.enums
将有枚举的对象插入到数据库
MP自动填充功能
添加配置类
@Component
public class MyMetaObjectHandler implements MetaObjectHandler
@Override
public void insertFill(MetaObject metaObject)
this.setFieldValByName("created", new Date(), metaObject);
this.setFieldValByName("updated", new Date(), metaObject);
@Override
public void updateFill(MetaObject metaObject)
this.setFieldValByName("updated", new Date(), metaObject);
为实体类添加注解
@Data
@Accessors(chain = true)
public class Product
private Long id;
private String name;
private Integer price;
//在插入数据时自动填充
@TableField(fill = FieldFill.INSERT)
private Date created;
//在插入和更新操作时自动填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated;
@Version//用来标识乐观锁版本号字段
private Integer version;
测试
//测试
Product product = new Product();
product.setName("cake").setId(3L).setPrice(66);
int insert = productMapper.insert(product);
System.out.println(insert);
MybatisPlus学习总结(下)
MybatisPlus学习总结(下)
一、条件构造器
在MP中,Wrapper接口的实现类关系如下:
## mybatis plus基本操作
查询方式 说明
setSqlSelect 设置 SELECT 查询字段
where WHERE 语句,拼接 + WHERE 条件
and AND 语句,拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值)
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last("LIMIT 1")
注意! xxNew 都是另起 ( ... ) 括号包裹。
1.1 allEq
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
个别参数说明: params : key 为数据库字段名, value 为字段值 null2IsNull : 为 true 则在 map 的 value 为
null 时调用 isNull 方法,为 false 时则忽略 value 为 null 的
- 例1: allEq({id:1,name:“老王”,age:null}) —> id = 1 and name = ‘老王’ and age is null
- 例2: allEq({id:1,name:“老王”,age:null}, false) —> id = 1 and name = '老王’
allEq(BiPredicate<R, V> filter, Map<R, V> params)
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean
null2IsNull)
个别参数说明: filter : 过滤函数,是否允许字段传入比对条件中 ,params 与 null2IsNull : 同上
- 例1: allEq((k,v) -> k.indexOf(“a”) > 0, {id:1,name:“老王”,age:null}) —> name = ‘老王’
and age is null- 例2: allEq((k,v) -> k.indexOf(“a”) > 0, {id:1,name:“老王”,age:null}, false) —> name =
'老王’
代码示例:
@Test
public void testAllEq(){
Map<String,Object> params = new HashMap<>();
params.put("name", "李四");
params.put("age", "20");
params.put("password", null);
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,user_name,name,age,email FROM tb_user WHERE (password IS NULL AND name = ? AND age = ?)
// wrapper.allEq(params);
//SELECT id,user_name,name,age,email FROM tb_user WHERE (name = ? AND age = ?)
// wrapper.allEq(params,false);
//过滤函数表示是否允许字段传入比对条件中 params
//SELECT id,user_name,name,age,email FROM tb_user WHERE (password IS NULL AND age = ?)
wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id") || k.equals("password")) , params);
List<User> users = userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
1.2 基本比较操作
@Test
public void testEq() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,user_name,name,age,email FROM tb_user WHERE (password = ? AND age >= ? AND name IN (?,?,?))
wrapper.eq("password", "123456")
.ge("age", 20)
.in("name", "李四", "王五", "赵六");
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
结果:
1.3 模糊查询
代码示例:
@Test
public void testLike(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,user_name,name,age,email FROM tb_user WHERE (name LIKE ?)
// 参数:%五(String)
wrapper.likeLeft("name", "五");
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
结果:
1.4 排序
代码示例:
@Test
public void testOrderByAgeDesc(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//按照年龄倒序排序
// SELECT id,user_name,name,age,email FROM tb_user ORDER BY age DESC
wrapper.orderByDesc("age");
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
结果:
1.5 逻辑查询
代码示例:
@Test
public void testOr(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
// SELECT id,user_name,name,age,email FROM tb_user WHERE (name = ? OR age = ?)
wrapper.eq("name", "王五").or().eq("age", 21);
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
结果:
1.6 select
在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段。
代码示例:
@Test
public void testSelect(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,name,age FROM tb_user WHERE (name = ? OR age = ?)
wrapper.eq("name", "王五")
.or()
.eq("age", 21)
.select("id","name","age"); //指定查询的字段
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
结果:
二、ActiveRecord
在Mybatis-Plus中提供了ActiveRecord
的模式,支持 ActiveRecord 形式调用,实体类只需继承 Model
类即可实现基本 CRUD 操作,简单来说就是一个实体类继承Model类,并通过注解与数据库的表名进行关联,这样就可以通过实体类直接进行表的简单增删改查操作
实现步骤:
-
Mapper对象要先继承BaseMapper对象
public interface UserMapper extends BaseMapper<User> { }
-
实体对象要继承Model对象
@Data @TableName("tb_user") public class User extends Model<User> { @TableId(value = "id",type = IdType.AUTO) private Long id; private String userName; @TableField(select = false)//查询的时候不查询该字段 private String password; private String name; private Integer age; private String email; @TableField(exist = false) private String address; //在数据库表中是不存在的 }
-
代码测试
@Test public void testSelectById(){ User user = new User(); user.setId(16L); //SELECT id,user_name,name,age,email FROM tb_user WHERE id=? User user1 = user.selectById(); System.out.println(user1); } @Test public void testInsert(){ User user = new User(); user.setUserName("diaochan"); user.setPassword("123456"); user.setAge(20); user.setName("貂蝉"); user.setEmail("diaochan@itcast.cn"); //INSERT INTO tb_user ( user_name, password, name, age, email ) VALUES ( ?, ?, ?, ?, ? ) // 调用AR的insert方法进行插入数据 boolean insert = user.insert(); System.out.println("result => " + insert); }
三、插件
3.1 mybatis的插件机制
参考博客:https://blog.csdn.net/oneby1314/article/details/116289510
3.2 常用插件配置
1、 防止全表更新与删除插件
- 针对 update 和 delete 语句 作用: 阻止恶意的全表更新删除
- SpringBoot配置:
如果进行全表的更新和删除操作则会抛出:@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 防止全表更新与删除 // 针对 update 和 delete 语句 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; }
MybatisPlusException: Prohibition of table update operation
2、分页插件
-
插件配置:(SpringBoot)
@Configuration public class MybatisPlusConfig { // 配置分页插件,最新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return interceptor; } }
-
测试:
// 测试分页查询,前提:配置分页插件 @Test public void testSelectPage(){ Page<User> page = new Page<>(3,2); //查询 QueryWrapper<User> wrapper = new QueryWrapper<>(); //设置查询条件 // wrapper.like("email", "itcast");//like表示模糊查询 IPage<User> iPage = userMapper.selectPage(page, wrapper); System.out.println("数据总条数: " + iPage.getTotal()); System.out.println("数据总页数: " + iPage.getPages()); System.out.println("当前页数: " + iPage.getCurrent()); List<User> records = iPage.getRecords(); for (User record : records) { System.out.println(record); } }
执行结果:
3、乐观锁插件
-
插件配置:(SpringBoot)
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加乐观锁插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }
-
为表添加version字段,并且设置初始值为1
ALTER TABLE `tb_user` ADD COLUMN `version` int(10) NULL AFTER `email`; UPDATE `tb_user` SET `version`='1';
-
为User实体对象添加version字段,并且添加@Version注解
@Version private Integer version;
-
测试
@Test public void testUpdateVersion(){ User user = new User(); user.setId(2L);// 查询条件 User userVersion = user.selectById(); user.setAge(23); // 更新的数据 user.setVersion(userVersion.getVersion()); // 当前的版本信息 boolean result = user.updateById(); System.out.println("result => " + result); }
执行结果:
-
注意:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1
newVersion 会回写到 entity 中
四、MybatisPlus 扩展
4.1 Sql注入器
SQL注入器主要用于扩充BaseMapper中的方法。以扩展findAll方法为例。
-
编写MyBaseMapper
public interface MyBaseMapper<T> extends BaseMapper<T> { List<T> findAll(); }
其他的Mapper都可以继承该Mapper,这样实现了统一的扩展
public interface UserMapper extends MyBaseMapper<User> { }
-
编写MySqlInjector
如果直接继承
AbstractSqlInjector
的话,原有的BaseMapper中的方法将失效,所以我们选择继承DefaultSqlInjector
进行扩展。public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = super.getMethodList(mapperClass); // 再扩充自定义的方法 methodList.add(new FindAll()); return methodList; } }
-
编写FindAll
public class FindAll extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { String sqlMethod = "findAll"; String sql = "select * from " + tableInfo.getTableName(); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.SpringBoot项目配置模板(SpringBoot + MybatisPlus + Druid)