MybatisPlus
简介:是一个 MyBatis 的增强工具
官方网站:https://baomidou.com/
官方文档:https://baomidou.com/pages/24112f/
基本使用
导入包:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
数据源配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
# 如果使用 MySQL8 驱动不同,增加时区的配置 serverTimezone=GMT%2B8
url: jdbc:mysql://xx:3306/数据库?useSSL=false&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 默认日志
mapper-locations: classpath:/mapper/**/*.xml
基本使用:
启动类增加扫描mapper文件夹,省略@mapper注解
@MapperScan("指定Mapper接口所在的包")
mapper层编写
@Repository // 持久层
public interface BillMapper extends BaseMapper<Bill> {}
service层编写
需要继承IService
public interface BillService extends IService<Bill> {}
service实现类编写
@Service
public class BillServiceImpl extends ServiceImpl<BillMapper, Bill> implements BillService {}
常用注解
TableName
实体对应表名
@TableName("wms_ware_info")
TableId
主键注解
@TableId(value = "bid",type = IdType.AUTO)
type类型常见值:
- AUTO 数据库ID自增
- NONE 无状态
- INPUT insert前自行set主键值
- ASSIGN_ID 雪花算法生产id
- ASSIGN_UUID UUID
- ID_WORKER 分布式全局唯一ID,长整型类型,32位UUID字符串
- ID_WORKER_STR 分布式全局唯一ID 字符串类型
注意:雪花算法:分布式ID生成算法,结果是一个long型的ID
TbaleField
设置实体类中的属性名和字段名注解
@TbaleField("user_name")
# 表中新增字段 create_time,update_time 创建时间/修改时间
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
# 如果为null,字段也更新为null
@TableField(updateStrategy = FieldStrategy.IGNORED)
# 表示不是数据库里的字段
@TableField(exist = false)
# MyMetaObjectHandler类
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
}
// 更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
TableLogic
逻辑删除 (假删除)
// 逻辑删除 value 1:未删除的值 、delval 0 :删除后的值
@TableLogic(value = "1", delval = "0")
private Integer isDeleted; // 默认0 未删除 1已删除
# 配置文件
logic-delete-field: flag # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
通用枚举
设置枚举类、实体字段设置
@Getter
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
@EnumValue //将注解所标识的属性的值存储到数据库中
private int sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
# 实体字段设置
private SexEnum sex;
# 配置文件 - 增加配置
# 扫描通用枚举的包
type-enums-package: com.atguigu.mybatisplus.enums
测试
@Test
public void test(){
User user = new User();
user.setName("admin");
user.setSex(SexEnum.MALE); // 保持数据库是key值
int result = userMapper.insert(user);
}
基本API
Mapper层
// 1. 查询全部数据
List<Emp> emps = empMapper.selectList(null);
// 2. 新增
Emp emp1 = new Emp();
emp1.setAge(13);
int insert = empMapper.insert(emp1);
// 3. 删除
int i = empMapper.deleteById(9L); // 参数id
// 3.1 批量删除
int i1 = empMapper.deleteBatchIds(Arrays.asList(11L, 10L));
// 3.2 根据条件删除 注:key 是表字段
Map<String,Object> mapdel = new HashMap<>();
mapdel.put("name","修改的");
empMapper.deleteByMap(mapdel);
// 4. 修改
Emp emp2 = new Emp();
emp2.setId(12);
emp2.setName("修改的");
int i2 = empMapper.updateById(emp2);
// 5. 查询
Emp empO = empMapper.selectById(12L);
List<Emp> all = empMapper.selectBatchIds(Arrays.asList(1L, 2L));
System.out.println(all);
// 5.1 根据查询条件查询,注:key 是表字段
Map<String,Object> map = new HashMap<>();
map.put("name","修改的");map.put("age","13");
List<Emp> bills = empMapper.selectByMap(map);
Service层
// 1.查出总记录数
long count = empService.count();
// long count2 = empService.count(new QueryWrapper<Emp>());
System.out.println(count);
// 2.根据主键id查询
Emp byId = empService.getById(3L);
// 3.查询全部
List<Emp> all = empService.list();
// List<Emp> all2 = empService.list(new QueryWrapper<Emp>());
System.out.println(all);
// 4.新增
empService.save(new Emp());
// 5.修改
empService.updateById(new Emp());
// empService.update(new QueryWrapper<Emp>());
// 6.新增或修改 , 有主键代表修改,否则新增
empService.saveOrUpdate(new Emp());
// 7.批量新增
List<Emp> alls = Arrays.asList(
new Emp("王1", 1),
new Emp("琳2", 33),
new Emp("凯3", 32));
boolean b = empService.saveBatch(alls);
// 8.批量新增或修改 , 有主键代表修改,否则新增
empService.saveOrUpdateBatch(alls);
// 9.根据主键 删除
empService.removeById(20L);
empService.remove(new QueryWrapper<Emp>());
// 10.批量删除
empService.removeBatchByIds(Arrays.asList(11L, 10L));
条件构造器
Wrapper介绍:
Wrapper 条件构造抽象类
AbstractWrapper 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper 查询条件封装
- UpdateWrapper 修改条件
- AbstractLambdaWrapper 使用Lambda 语法,它还有LambdaQueryWrapper 、LambdaUpdateWrapper
QueryWrapper:
查询条件
//查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
QueryWrapper<User> q = new QueryWrapper<>();
q.like("username","a").between("age",20,30).isNotNull("email");
//排序条件 orderByDesc 降序 orderByAsc 升序
q.orderByDesc("age").orderByAsc("id");
List<User> users = userMapper.selectList(q);
users.forEach(System.out::println);
删除条件
QueryWrapper<User> q = new QueryWrapper<>();
q.isNull("email");
int result = userMapper.delete(q);
System.out.println(result > 0 ? "删除成功!" : "删除失败!");
条件的优先级
QueryWrapper<User> q = new QueryWrapper();
q.select("bill_name","bill_num","pay"); // select查询 只显示这几个字段,其他字段不显示
// ge 大于等于 gt 大于 le 小于等于 lt 小于
q.gt("bill_num",4)
.and(i->{
i.like("bill_name","a").or().isNull("pay");
}).isNull("pay");
List<Map<String, Object>> maps = userMapper.selectMaps(q);
子查询
QueryWrapper<User> q = new QueryWrapper<>();
q.inSql("uid", "select uid from 表 where uid <= 100");
List<User> list = userMapper.selectList(q);
UpdateWrapper:
UpdateWrapper<User> u = new UpdateWrapper<>();
u.like("username","a")
.set("email","修改的字段").set("bill_name","修改的字段2");
int result = userMapper.update(null, u);
System.out.println(result > 0 ? "修改成功!" : "修改失败!");
condition:
String a = "a";
Integer b = null;
Integer c = 30;
QueryWrapper<User> q = new QueryWrapper<>();
// 参数 1 boolean值,满足条件会执行
q.like(StringUtils.isNotBlank(a), "user_name", a)
.ge(b != null, "age", b)
.le(c != null, "age", c);
List<User> list = userMapper.selectList(q);
LambdaQueryWrapper:
String a = "a";
Integer b = null;
Integer c = 30;
LambdaQueryWrapper<User> q = new LambdaQueryWrapper<>();
q.like(StringUtils.isNotBlank(a), User::getName, a)
.ge(b != null, User::getAge, b)
.le(c != null, User::getAge, c);
List<User> list = userMapper.selectList(q);
LambdaUpdateWrapper:
LambdaUpdateWrapper<User> u = new LambdaUpdateWrapper<>();
u.like(User::getName, "a").isNull(User::getEmail));
u.set(User::getName, "小黑").set(User::getEmail,"xxx");
int result = userMapper.update(null, u);
常用插件
分页
配置类
@Configuration
@MapperScan("com.yan.mybatis.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件 - 指向数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试
@Test
public void test1(){
// 参数:当前页、显示条数、查询条件
Page<Bill> page = billMapper.selectPage(new Page<>(1, 5), null);
List<Bill> users = page.getRecords();
users.forEach(System.out::println);
System.out.println(page.getRecords()); // 获取记录数
System.out.println(page.getSize()); // 获取显示条数
System.out.println(page.getCurrent()); // 获取当前页的页码
System.out.println(page.getPages()); // 获取总页码
System.out.println(page.getTotal()); // 获取总记录数
boolean b1 = page.hasNext(); // 是否有下一页
boolean b2 = page.hasPrevious(); // 是否有上一页
}
自定义分页
# Mapper层定义
Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
# XMl定义
<select id="selectPageVo" resultType="User">
select * from t_user where age > #{age}
</select>
# 测试
Page<User> page = userMapper.selectPageVo(new Page<User>(1,2), 20);
List<User> users = page.getRecords();
乐观锁
乐观锁:保持乐观状态,无论干什么都不会上锁,如果有问题就再次更新值测试。(版本号version)
悲观锁:总是认为总会出现问题,无论干什么都会上锁后再去操作。
配置类
@Configuration
@MapperScan("com.yan.mybatis.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件 - 指向数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
实体字段增加注解
@Version // 标识乐观锁版本号字段
private String version;
测试
@Test
public void test1(){
// 1.B1 获得的值
Bill b1 = billMapper.selectById(16);
System.out.println("B1 :" + b1.getMoney());
// 2.B2 获得的值
Bill b2 = billMapper.selectById(16);
System.out.println("B2 :" + b1.getMoney());
// 3.B1 获得的值 + 50
b1.setMoney(b1.getMoney() + 50);
billMapper.updateById(b1);
// 4.B2 获得的值 - 30
b2.setMoney(b2.getMoney()-30);
int result = billMapper.updateById(b2);
if (result == 0) {
// 操作失败,重试
Bill b = billMapper.selectById(16);
b.setMoney(b.getMoney() - 30);
billMapper.updateById(b);
}
// 5.查看 16这条数据 值 - 乐观锁
Bill bill = billMapper.selectById(16);
System.out.println("Bill :" + bill.getMoney());
}
注意:
整数类型下 newVersion = oldVersion + 1
newVersion 会回写到 entity 中
仅支持 updateById(id) 与 update(entity, wrapper) 方法
在 update(entity, wrapper) 方法下, wrapper 不能复用
通用枚举
枚举类
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
/**
* 枚举
*/
@Getter
public enum SexEnum {
ONE(0, "未付款"),
TWO(1, "已付款");
@EnumValue // 将注解所标识的属性的值存储到数据库中
private int index;
private String name;
SexEnum(Integer index, String name) {
this.index = index;
this.name = name;
}
}
配置文件需要配置
type-enums-package: cn.good.yan.pojo.conf
实体需要引用
private SexEnum pay;
代码生成器
导入依赖包
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
测试
// 代码生成器
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8", "用户名", "密码")
.globalConfig(builder -> {
builder.author("yan") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("G://xx"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.yan") // 设置父包名
.moduleName("mybatis") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "G://xx/mapper")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("pms_spu_comment") // 设置需要生成的表名
.addTablePrefix("t_", "pms_"); // 设置过滤表前缀
})
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
多数据库
适用场景:读写分离、一主一从、多库
导入依赖包
<!-- 多数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
更改配置
spring:
datasource:
# 配置多数据源
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false 如:true未匹配到指定数据源时抛异常,false使用默认数据源,也就是主数据源
strict: false
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/数据库1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
username: 用户名
password: 密码
slave:
url: jdbc:mysql://127.0.0.1:3306/数据库2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
username: 用户名
password: 密码
指定操作数据源
// 指定操作的数据源,master - 也可以放在具体的方法上,和类上,就近原则取对应的数据源
@DS("master")
public interface UserService extends IService<User> {}
// ---
@DS("slave")
public interface BillService extends IService<Bill> {}
测试
@Autowired
private BillService billService;
@Autowired
private UserService userService;
@Test
public void test(){
Bill b1 = billService.getById(16);
System.out.println(b1);
User u1 = userService.getById(2);
System.out.println(u1);
}
MyBatisX插件
MyBatisX一款基于 IDEA 的快速开发插件,简化我们开发。在IDEA的插件里进行下载MyBatisX。
IDEA中与数据库建立链接:
找到我们要生成的表
然后点击,填写第一页
填写第二页
操作完成 - 快速生成对应的文件
然后我们开发过程中快速生成CRUD操作:
选择这个,可以快速生产对应的代码和XML映射文件
感谢观看,代码案例:优秀的颜/MybatisPlus