目录
MyBatisPlus
MP特点
MP框架结构
MP使用准备
导入依赖
springboot整合mybatisplus配置文件
定义好实体类User后编辑mapper接口
@Mapper与@MapperScan("包名")区别
MP基本操作
新增操作
删除操作
通过id删除用户
通过map作为条件删除
通过多个id实现删除
更新用户
通过id进行用户更新
查询用户
根据id查询用户
根据多个id查询用户
根据map集合作为条件查询用户
通用Service接口
一些操作
查询总记录数
批量添加数据
MP常用注解
雪花算法
前言
垂直分表
水平分表
条件构造器
继承结构
使用条件构造器实现查询操作
查询所有用户
根据构造器查询主键字段集合
根据条件构造器查询多用户
关于模糊查询
in查询
转义字符方式查询并排序
条件构造器实现删除操作
使用条件构造器实现修改操作
条件的优先级
组装select语句
组装子查询
动态sql查询
使用updateWrapper实现修改功能
LambdaQueryWrapper
LambdaUpdateWrapper
MP的分页插件
配置配置类(必然)
MP实现分页
自定义分页功能
MP乐观锁
在需要改变的实体类中加version注解
添加乐观锁插件
测试
通用枚举
MP自动填充功能
添加配置类
为实体类添加注解
测试
含义:mybatis-plus是个mybatis的增强工具,在mybatis的基础上只做增强不做改变,为简化开发,提高效率而生
注意:我们可以直接在mybatis的基础上直接去集成mybatisplus,这样并不会影响mybatis的功能,同时我们也可以使用他所提供的功能。
理解:
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.projectlombok
lombok
true
com.baomidou
mybatis-plus-boot-starter
3.5.1
mysql
mysql-connector-java
runtime
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
//使用MP提供的通用mapper——BaseMapper
//BaseMapper里的泛型表示实体类的类型
@Mapper
public interface UserMapper extends BaseMapper {
}
注意:
注意:使用前注入userMapper
User user = new User();
user.setName("lili").setAge(23).setEmail("[email protected]");
int insert = userMapper.insert(user);
System.out.println(insert);
//mybatis-plus会自动获取id
System.out.println(user.getId());
int i = userMapper.deleteById(7);
System.out.println(i);
Map map=new HashMap<>();
map.put("name", "张三");
map.put("age", 23);
//删除name为张三,age为23的人
int i = userMapper.deleteByMap(map);
System.out.println(i);
List list = Arrays.asList(1L, 2L, 3L);
int i = userMapper.deleteBatchIds(list);
System.out.println(i);
User user = new User();
user.setId(3L).setName("lan").setEmail("[email protected]");
//根据id修改元素
int i = userMapper.updateById(user);
System.out.println(i);
User user = userMapper.selectById(1L);
System.out.println(user);
List list = Arrays.asList(1L, 2L, 3L);
List users = userMapper.selectBatchIds(list);
users.forEach(System.out::println);
HashMap map = new HashMap<>();
map.put("name", "lan");
map.put("age", 28);
List users = userMapper.selectByMap(map);
//list会直接打印对象数组
System.out.println(users);
说明:通用Service封装了IService接口,进一步封装了CRUD采用get查询单行,remove删除,list查询集合,page分页等前缀命名方式,区分Mapper层,避免混淆
//service接口
public interface UserService extends IService {
}
//service实现类
@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
}
注意:
//查询总记录数
long count = userService.count();
System.out.println(count);
ArrayList 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);
//设置实体类所对应的表名,若对象与表名一致,则表名中()可以省略
@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分布式主键生成算法,他保证不同表的主键的不重复性,以及相同表的主键的有序性
核心思想:
优点:整体上按照时间自增排序,并且整个分布式系统内不会产生id碰撞
作用:封装当前的条件
AbstractWrapper:用于条件查询封装,生成sql的where条件
AbstractLambdaWrapper
//通过条件构造器查询一个list集合,若没有条件则可以设置null(相当于查询所有)
List users = userMapper.selectList(null);
users.forEach(System.out::println);
//查询name为lei的主键字段
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "lei");
List
//查询用户名包含a,年龄在20-30之间,邮箱信息不为null的用户信息
QueryWrapper queryWrapper = new QueryWrapper<>();
//queryWrapper可以实现链式加载
queryWrapper.like("name", "a").between("age", 20, 30).isNotNull("email");
List users = userMapper.selectList(queryWrapper);
System.out.println(users);
//对象形式
User user = new User();
user.setAge(28);
QueryWrapper queryWrapper = new QueryWrapper<>(user);
List users = userMapper.selectList(queryWrapper);
System.out.println(users);
//user内的属性最终会以and形式拼接
//in查询,查询id为1,2,3的数据
Integer[] ids={1,2,3};
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.in("id",ids);
List users = userMapper.selectList(queryWrapper);
System.out.println(users);
//查询id>2的用户,按照年龄降序排序,若年两相同则按照id升序排序
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.gt("id", 2).orderByDesc("age").orderByAsc("id");
List users = userMapper.selectList(queryWrapper);
System.out.println(users);
转义字符
//删除邮箱地址为null的用户信息
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int i = userMapper.delete(queryWrapper);
System.out.println(i);
//将年龄>20并且用户名中包含a或邮箱为null的用户进行修改(默认情况下就是and连接)
//修改条件
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20).like("name", "a").or().isNull("email");
User user = new User();
user.setName("lei").setEmail("[email protected]");
int i = userMapper.update(user, queryWrapper);
System.out.println(i);
//将用户名中包含a并且(年龄大于20或邮箱为null)的用户信息进行修改
QueryWrapper 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("[email protected]");
int i = userMapper.update(user, queryWrapper);
System.out.println(i);
//查询出来一个以map为泛型的list集合
//查询用户名、年龄、邮箱信息
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.select("name","age","email");
List
//select * from t_user where id in(select id from t_user where id<=100)
//查询id<=100的用户信息
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from t_user where id<=100");
List users = userMapper.selectList(queryWrapper);
System.out.println(users);
String name=null;
String age="21";
//判断字符串是否为null或空串若为返回false,不为返回true
boolean pn = StringUtils.hasLength(name);
boolean pa = StringUtils.hasLength(age);
QueryWrapper queryWrapper = new QueryWrapper<>();
//判断属性是否为true,为true则执行该条件,不为则忽略该条件
queryWrapper.eq(pn,"name",name).eq(pa, "age", age);
List users = userMapper.selectList(queryWrapper);
System.out.println(users);
注意:queryWrapper.clear();为清除多余的条件,清除后queryWrapper可以继续使用
//查询用户名中包含a(年龄>20或邮箱为null)的员工信息
UpdateWrapper updateWrapper = new UpdateWrapper<>();
//修改条件
updateWrapper.like("name", "a").and(i->i.gt("age", 20).isNull("email"));
//修改内容
updateWrapper.set("name", "lala").set("email", "[email protected]");
int i = userMapper.update(null, updateWrapper);
System.out.println(i);
作用:防止我们太笨,而把字段名写错进而提供了一个函数式接口来访问我们实体类中的某一个属性,当我们把属性访问之后,那么他就可以自动的获取属性所对应的字段名,来当作作为条件的哪个字段
String name="a";
Integer ageBegin=null;
Integer ageEnd=30;
//主要避免了名称写错进而提供了直接访问表达式::
LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(StringUtils.isNotBlank(name), User::getName,name)
.ge(ageBegin!=null, User::getAge,ageBegin)
.le(ageEnd!=null, User::getAge,ageEnd);
List users = userMapper.selectList(lambdaQueryWrapper);
System.out.println(users);
//查询用户名中包含a(年龄>20或邮箱为null)的员工信息
LambdaUpdateWrapper 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, "[email protected]");
int i = userMapper.update(null, updateWrapper);
System.out.println(i);
@Configuration
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//创建mybatisplus拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//向拦截器中添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
//测试类内
//两个参数——当前页页码,每页信息条数
Page page = new Page<>(2,3);
//两个参数——分页对象,条件构造器
userMapper.selectPage(page, null);//因为我对所有的查询所以条件构造器为null——返回值还为page
//获取当前页数据
List 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 selectPageVo(@Param("page") Page page,@Param("age") Integer age);
//自定义配置文件sql
//测试类
Page page = new Page<>(2, 2);
userMapper.selectPageVo(page,20);
//获取当前页数据
List 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());
@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
将有枚举的对象插入到数据库
@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);