基本使用
导入包:
<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 小于 eq 等于 ne 不等 // between 范围 notBetween in notIn // like notLike likeLeft (LIKE '%值’) likeRight (LIKE’值%’) // isNull isNotNull // inSql 字段IN ( sql语句) notInSql // groupBy 分组 orderByAsc orderByDesc orderBy 例:orderBy(true,true,“id”,“name”)—–>order by id ASC, name ASC // having exists notExists or and 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映射文件