原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10125279.html
SpringBoot 整合 MyBatis-plus
步骤
第一步:添加必要的依赖
第一种是在已存在 MyBatis 的情况下,直接添加 mybatis-plus 包即可。
| <dependency> |
| <groupId>com.baomidou</groupId> |
| <artifactId>mybatis-plus</artifactId> |
| <version>2.1.8</version> |
| </dependency> |
第二种是直接添加 mybatis-plus 的 starter,它会自动导入 mybatis 的依赖包及其他相关依赖包
| <dependency> |
| <groupId>com.baomidou</groupId> |
| <artifactId>mybatis-plus-boot-starter</artifactId> |
| <version>3.0.1</version> |
| </dependency> |
第二步:添加必要的配置
注意:Mybatis-plus 是 MyBatis 的再封装,添加 MyBatis-plus 之后我们的设置针对的应该是 MyBatis-plus,而不是 MyBatis。
| mybatis-plus: |
| mapper-locations: classpath*:/mapper/*.xml |
| type-aliases-package: com.example.springbootdemo.entity |
| type-aliases-super-type: java.lang.Object |
| type-handlers-package: com.example.springbootdemo.typeHandler |
| type-enums-package: com.example.springbootdemo.enums |
第三步:添加必要的配置类
| @EnableTransactionManagement |
| @Configuration |
| @MapperScan("com.example.springbootdemo.plusmapper") |
| public class MyBatisPlusConfig { |
| |
| |
| @Bean |
| public PaginationInterceptor paginationInterceptor() { |
| return new PaginationInterceptor(); |
| } |
| |
| } |
第四步:定义实体
| @Data |
| @Builder |
| @ToString |
| @EqualsAndHashCode |
| @NoArgsConstructor |
| @AllArgsConstructor |
| @TableName(value = "ANIMAL") |
| public class Animal { |
| @TableId(value = "ID",type = IdType.AUTO) |
| private Integer id; |
| @TableField(value = "NAME",exist = true) |
| private String name; |
| @TableField(value = "TYPE",exist = true) |
| private AnimalType type; |
| @TableField(value = "SEX",exist = true) |
| private AnimalSex sex; |
| @TableField(value = "MASTER",exist = true) |
| private String master; |
| } |
| public enum AnimalType implements IEnum { |
| CAT("1","猫"),DOG("2","狗"),TIGER("3","虎"),MOUSE("4","鼠"),MONKEY("5","猴"),LOAN("6","狮"),OTHER("7","其他"); |
| private final String value; |
| private final String desc; |
| AnimalType(final String value,final String desc){ |
| this.value=value; |
| this.desc = desc; |
| } |
| @Override |
| public Serializable getValue() { |
| return value; |
| } |
| public String getDesc() { |
| return desc; |
| } |
| } |
| public enum AnimalSex implements IEnum { |
| MALE("1","公"),FEMALE("2","母"); |
| private final String value; |
| private final String desc; |
| AnimalSex(final String value,final String desc){ |
| this.value = value; |
| this.desc = desc; |
| } |
| @Override |
| public Serializable getValue() { |
| return value; |
| } |
| public String getDesc() { |
| return desc; |
| } |
| } |
第五步:定义 mapper 接口
| public interface AnimalRepository extends BaseMapper<Animal> { |
| } |
解说:使用 MyBatis Plus 后 Mapper 只要继承 BaseMapper 接口即可,即使不添加 XML 映射文件也可以实现该接口提供的增删改查功能,还可以配合 Wrapper 进行条件操作,当然这些操作都仅仅限于单表操作,一旦涉及多表联查,那么还是乖乖添加 **Mapper.xml 来自定义 SQL 吧!!!
第六步:定义 service(重点)
| @Service |
| @Log4j2 |
| public class AnimalService { |
| |
| @Autowired |
| private AnimalRepository animalRepository; |
| |
| |
| public ResponseEntity<Animal> addAnimal(final Animal animal) { |
| animalRepository.insert(animal); |
| return ResponseEntity.ok(animal); |
| } |
| |
| |
| public ResponseEntity<Integer> deleteAnimalById(final int id){ |
| return ResponseEntity.ok(animalRepository.deleteById(id)); |
| } |
| |
| public ResponseEntity<Integer> deleteAnimals(final Animal animal){ |
| return ResponseEntity.ok(animalRepository.delete(packWrapper(animal, WrapperType.QUERY))); |
| } |
| |
| public ResponseEntity<Integer> deleteAnimalsByIds(List<Integer> ids){ |
| return ResponseEntity.ok(animalRepository.deleteBatchIds(ids)); |
| } |
| |
| public ResponseEntity<Integer> deleteAnimalsByMap(final Animal animal){ |
| Map<String, Object> params = new HashMap<>(); |
| if(Objects.nonNull(animal.getId())){ |
| params.put("ID",animal.getId()); |
| } |
| if(StringUtils.isNotEmpty(animal.getName())){ |
| params.put("NAME", animal.getName()); |
| } |
| if(Objects.nonNull(animal.getType())){ |
| params.put("TYPE", animal.getType()); |
| } |
| if(Objects.nonNull(animal.getSex())){ |
| params.put("SEX", animal.getSex()); |
| } |
| if (StringUtils.isNotEmpty(animal.getMaster())){ |
| params.put("MASTER", animal.getMaster()); |
| } |
| return ResponseEntity.ok(animalRepository.deleteByMap(params)); |
| } |
| |
| |
| public ResponseEntity<Integer> updateAnimals(final Animal animal, final Animal condition){ |
| return ResponseEntity.ok(animalRepository.update(animal, packWrapper(condition, WrapperType.UPDATE))); |
| } |
| |
| public ResponseEntity<Integer> updateAnimal(final Animal animal){ |
| Wrapper<Animal> animalWrapper = new UpdateWrapper<>(); |
| ((UpdateWrapper<Animal>) animalWrapper).eq("id",animal.getId()); |
| return ResponseEntity.ok(animalRepository.update(animal, animalWrapper)); |
| } |
| |
| |
| public ResponseEntity<Animal> getAnimalById(final int id){ |
| return ResponseEntity.ok(animalRepository.selectById(id)); |
| } |
| |
| public ResponseEntity<Animal> getOneAnimal(final Animal animal){ |
| return ResponseEntity.ok(animalRepository.selectOne(packWrapper(animal, WrapperType.QUERY))); |
| } |
| |
| public ResponseEntity<List<Animal>> getAnimals(final Animal animal){ |
| return ResponseEntity.ok(animalRepository.selectList(packWrapper(animal, WrapperType.QUERY))); |
| } |
| |
| public ResponseEntity<List<Animal>> getAnimalsByIds(List<Integer> ids){ |
| return ResponseEntity.ok(animalRepository.selectBatchIds(ids)); |
| } |
| |
| public ResponseEntity<List<Animal>> getAnimalsByMap(final Animal animal){ |
| Map<String, Object> params = new HashMap<>(); |
| if(Objects.nonNull(animal.getId())){ |
| params.put("ID",animal.getId()); |
| } |
| if(StringUtils.isNotEmpty(animal.getName())){ |
| params.put("NAME", animal.getName()); |
| } |
| if(Objects.nonNull(animal.getType())){ |
| params.put("TYPE", animal.getType()); |
| } |
| if(Objects.nonNull(animal.getSex())){ |
| params.put("SEX", animal.getSex()); |
| } |
| if (StringUtils.isNotEmpty(animal.getMaster())){ |
| params.put("MASTER", animal.getMaster()); |
| } |
| return ResponseEntity.ok(animalRepository.selectByMap(params)); |
| } |
| |
| public ResponseEntity<List<Map<String, Object>>> getAnimalMaps(final Animal animal){ |
| return ResponseEntity.ok(animalRepository.selectMaps(packWrapper(animal, WrapperType.QUERY))); |
| } |
| |
| |
| public ResponseEntity<Integer> getCount(final Animal animal){ |
| return ResponseEntity.ok(animalRepository.selectCount(packWrapper(animal, WrapperType.QUERY))); |
| } |
| |
| |
| public ResponseEntity<Page<Animal>> getAnimalPage(final Animal animal,final int pageId,final int pageSize){ |
| Page<Animal> page = new Page<>(); |
| page.setCurrent(pageId); |
| page.setSize(pageSize); |
| return ResponseEntity.ok((Page<Animal>) animalRepository.selectPage(page,packWrapper(animal, WrapperType.QUERY))); |
| } |
| |
| private Wrapper<Animal> packWrapper(final Animal animal, WrapperType wrapperType){ |
| switch (wrapperType){ |
| case QUERY: |
| QueryWrapper<Animal> wrapper = new QueryWrapper<>(); |
| if (Objects.nonNull(animal.getId())) |
| wrapper.eq("ID", animal.getId()); |
| if (StringUtils.isNotEmpty(animal.getName())) |
| wrapper.eq("name", animal.getName()); |
| if (Objects.nonNull(animal.getType())) |
| wrapper.eq("type", animal.getType()); |
| if (Objects.nonNull(animal.getSex())) |
| wrapper.eq("sex", animal.getSex()); |
| if (StringUtils.isNotEmpty(animal.getMaster())) |
| wrapper.eq("master", animal.getMaster()); |
| return wrapper; |
| case UPDATE: |
| UpdateWrapper<Animal> wrapper2 = new UpdateWrapper<>(); |
| if (Objects.nonNull(animal.getId())) |
| wrapper2.eq("ID", animal.getId()); |
| if (StringUtils.isNotEmpty(animal.getName())) |
| wrapper2.eq("name", animal.getName()); |
| if (Objects.nonNull(animal.getType())) |
| wrapper2.eq("type", animal.getType()); |
| if (Objects.nonNull(animal.getSex())) |
| wrapper2.eq("sex", animal.getSex()); |
| if (StringUtils.isNotEmpty(animal.getMaster())) |
| wrapper2.eq("master", animal.getMaster()); |
| return wrapper2; |
| case QUERYLAMBDA: |
| LambdaQueryWrapper<Animal> wrapper3 = new QueryWrapper<Animal>().lambda(); |
| if (Objects.nonNull(animal.getId())) |
| wrapper3.eq(Animal::getId, animal.getId()); |
| if (StringUtils.isNotEmpty(animal.getName())) |
| wrapper3.eq(Animal::getName, animal.getName()); |
| if (Objects.nonNull(animal.getType())) |
| wrapper3.eq(Animal::getType, animal.getType()); |
| if (Objects.nonNull(animal.getSex())) |
| wrapper3.eq(Animal::getSex, animal.getSex()); |
| if (StringUtils.isNotEmpty(animal.getMaster())) |
| wrapper3.eq(Animal::getMaster, animal.getMaster()); |
| return wrapper3; |
| case UPDATELAMBDA: |
| LambdaUpdateWrapper<Animal> wrapper4 = new UpdateWrapper<Animal>().lambda(); |
| if (Objects.nonNull(animal.getId())) |
| wrapper4.eq(Animal::getId, animal.getId()); |
| if (StringUtils.isNotEmpty(animal.getName())) |
| wrapper4.eq(Animal::getName, animal.getName()); |
| if (Objects.nonNull(animal.getType())) |
| wrapper4.eq(Animal::getType, animal.getType()); |
| if (Objects.nonNull(animal.getSex())) |
| wrapper4.eq(Animal::getSex, animal.getSex()); |
| if (StringUtils.isNotEmpty(animal.getMaster())) |
| wrapper4.eq(Animal::getMaster, animal.getMaster()); |
| return wrapper4; |
| default:return null; |
| } |
| } |
| } |
| enum WrapperType{ |
| UPDATE,UPDATELAMBDA,QUERY,QUERYLAMBDA; |
| } |
第七步:定义 controller
| @RestController |
| @RequestMapping("animal") |
| @Api(description = "动物接口") |
| @Log4j2 |
| public class AnimalApi { |
| @Autowired |
| private AnimalService animalService; |
| @RequestMapping(value = "addAnimal",method = RequestMethod.PUT) |
| @ApiOperation(value = "添加动物",notes = "添加动物",httpMethod = "PUT") |
| public ResponseEntity<Animal> addAnimal(final Animal animal){ |
| return animalService.addAnimal(animal); |
| } |
| @RequestMapping(value = "deleteAnimalById", method = RequestMethod.DELETE) |
| @ApiOperation(value = "删除一个动物",notes = "根据 ID 删除动物",httpMethod = "DELETE") |
| public ResponseEntity<Integer> deleteAnimalById(final int id){ |
| return animalService.deleteAnimalById(id); |
| } |
| @RequestMapping(value = "deleteAnimalsByIds",method = RequestMethod.DELETE) |
| @ApiOperation(value = "删除多个动物",notes = "根据 Id 删除多个动物",httpMethod = "DELETE") |
| public ResponseEntity<Integer> deleteAnimalsByIds(Integer[] ids){ |
| return animalService.deleteAnimalsByIds(Arrays.asList(ids)); |
| } |
| @RequestMapping(value = "deleteAnimals", method = RequestMethod.DELETE) |
| @ApiOperation(value = "删除动物",notes = "根据条件删除动物",httpMethod = "DELETE") |
| public ResponseEntity<Integer> deleteAnimalsByMaps(final Animal animal){ |
| return animalService.deleteAnimalsByMap(animal); |
| } |
| @RequestMapping(value = "deleteAnimals2", method = RequestMethod.DELETE) |
| @ApiOperation(value = "删除动物",notes = "根据条件删除动物",httpMethod = "DELETE") |
| public ResponseEntity<Integer> deleteAnimals(final Animal animal){ |
| return animalService.deleteAnimals(animal); |
| } |
| @RequestMapping(value = "getAnimalById",method = RequestMethod.GET) |
| @ApiOperation(value = "获取一个动物",notes = "根据 ID 获取一个动物",httpMethod = "GET") |
| public ResponseEntity<Animal> getAnimalById(final int id){ |
| return animalService.getAnimalById(id); |
| } |
| |
| |
| @RequestMapping(value = "getAnimalsByPage") |
| @ApiOperation(value = "分页获取动物们",notes = "分页获取所有动物", httpMethod = "GET") |
| public ResponseEntity<Page<Animal>> getAnimalsByPage(@RequestParam final int pageId, @RequestParam final int pageSize, final Animal animal) { |
| return animalService.getAnimalPage(animal==null?Animal.builder().build():animal, pageId, pageSize); |
| } |
| @RequestMapping(value = "updateAnimal") |
| @ApiOperation(value = "更新动物", notes = "根据条件更新",httpMethod = "POST") |
| public ResponseEntity<Integer> updateAnimals(final Animal animal){ |
| return animalService.updateAnimal(animal); |
| } |
| } |
高级功能
代码生成器
分页插件
第一步:添加必要的配置
| @EnableTransactionManagement |
| @Configuration |
| @MapperScan("com.example.springbootdemo.plusmapper") |
| public class MyBatisPlusConfig { |
| @Bean |
| public PaginationInterceptor paginationInterceptor() { |
| return new PaginationInterceptor(); |
| } |
| } |
第二步:添加 Mapper
| public interface AnimalRepository extends BaseMapper<Animal> { |
| } |
第三步:添加 service
| @Service |
| @Log4j2 |
| public class AnimalService { |
| @Autowired |
| private AnimalRepository animalRepository; |
| |
| public Page<Animal> getAnimalsByPage(int pageId,int pageSize) { |
| Page page = new Page(pageId, pageSize); |
| return (Page<Animal>)animalRepository.selectPage(page,null); |
| } |
| } |
逻辑删除
| 所谓逻辑删除是相对于物理删除而言的,MyBatis Plus 默认的删除操作是物理删除,即直接调用数据库的delete操作,直接将数据从数据库删除,但是,一般情况下,我们在项目中不会直接操作delete,为了保留记录,我们只是将其标记为删除,并不是真的删除,也就是需要逻辑删除,MyBatis Plus 也提供了实现逻辑删除的功能,通过这种方式可以将底层的delete操作修改成update操作。 |
第一步:添加必要的配置
| mybatis-plus: |
| global-config: |
| db-config: |
| logic-delete-value: 1 |
| logic-not-delete-value: 0 |
第二步:添加必要的配置类
| @Configuration |
| public class MyBatisPlusConfiguration { |
| |
| @Bean |
| public ISqlInjector sqlInjector() { |
| return new LogicSqlInjector(); |
| } |
| } |
第三步:添加字段 isDel 和注解
| @TableLogic |
| private Integer isDel; |
| 如此一来,我们再执行delete相关操作的时候,底层就会变更为update操作,将 isDel 值修改为1。 |
注意:通过此种方式删除数据后,实际数据还存在于数据库中,只是字段 isDel 值改变了,虽然如此,但是再通过 MyBatis Plus 查询数据的时候却会将其忽略,就好比不存在一般。
即通过逻辑删除的数据和物理删除的外在表现是一致的,只是内在机理不同罢了。
枚举自动注入
第一种方式
| 使用注解 @EnumValue |
| 使用方式:定义普通枚举,并在其中定义多个属性,将该注解标注于其中一个枚举属性之上,即可实现自动映射,使用枚举name传递,实际入库的却是添加了注解的属性值,查询也是如此,可以将库中数据与添加注解的属性对应,从而获取到枚举值name。 |
第二种方式
| Mybatis Plus 中定义了 IEnum 用来统筹管理所有的枚举类型,我们自定义的枚举只要实现 IEnum 接口即可,在 MyBatis Plus 初始化的时候,会自动在 MyBatis 中 handler 缓存中添加针对 IEnum 类型的处理器,我们的自定义的枚举均可使用这个处理器进行处理来实现自动映射。 |
步骤一:添加必要的配置
| mybatis-plus.type-enums-package: com.example.springbootdemo.enums |
步骤二:自定义枚举
| public enum AnimalType implements IEnum { |
| CAT("1","猫"),DOG("2","狗"),TIGER("3","虎"),MOUSE("4","鼠"),MONKEY("5","猴"),LOAN("6","狮"),OTHER("7","其他"); |
| private final String value; |
| private final String desc; |
| AnimalType(final String value,final String desc){ |
| this.value=value; |
| this.desc = desc; |
| } |
| @Override |
| public Serializable getValue() { |
| return value; |
| } |
| public String getDesc() { |
| return desc; |
| } |
| } |
注意:一定要实现 IEnum 接口,否则无法实现自动注入。
Sql 自动注入
性能分析插件
第一步:添加必要的配置
| @EnableTransactionManagement |
| @Configuration |
| @MapperScan("com.example.springbootdemo.plusmapper") |
| public class MyBatisPlusConfig { |
| |
| |
| @Bean |
| @Profile({"dev","test"}) |
| public PerformanceInterceptor performanceInterceptor() { |
| return new PerformanceInterceptor(); |
| } |
| } |
说明:
性能分析拦截器,用于输出每条 SQL 语句及其执行时间:
- maxTime:SQL 执行最大时长,超过自动停止运行,有助于发现问题。
- format:SQL SQL 是否格式化,默认 false。
注意:性能分析工具最好不要在生产环境部署,只在开发、测试环境部署用于查找问题即可。
乐观锁插件
第一步:添加必要的配置
| @EnableTransactionManagement |
| @Configuration |
| @MapperScan("com.example.springbootdemo.plusmapper") |
| public class MyBatisPlusConfig { |
| |
| |
| @Bean |
| public OptimisticLockerInterceptor optimisticLockerInterceptor() { |
| return new OptimisticLockerInterceptor(); |
| } |
| } |
第二步:添加 @Version
| @Version |
| private int version; |
注意:
- @Version 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime;
- 整数类型下 newVersion = oldVersion + 1;
- newVersion 会回写到 entity 中
- 仅支持 updateById(id) 与 update(entity, wrapper) 方法
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
实体主键配置
| @Getter |
| public enum IdType { |
| |
| |
| |
| AUTO(0), |
| |
| |
| |
| NONE(1), |
| |
| |
| |
| |
| INPUT(2), |
| |
| |
| |
| |
| |
| ID_WORKER(3), |
| |
| |
| |
| UUID(4), |
| |
| |
| |
| ID_WORKER_STR(5); |
| |
| private int key; |
| |
| IdType(int key) { |
| this.key = key; |
| } |
| } |
- AUTO:自增,适用于类似 MySQL 之类自增主键的情况
- NONE:不设置???
- INPUT:通过第三方进行逐渐递增,类似 Oracle 数据库的队列自增
- ID_WORKER:全局唯一 ID,当插入对象 ID 为空时,自动填充
- UUID:全局唯一 ID,当插入对象 ID 为空时,自动填充,一般情况下 UUID 是无序的
- ID_WORKER_STR:字符串全局唯一 ID,当插入对象 ID 为空时,自动填充
注意事项
| 最好不要和 devTools 一起使用,因为 devTools 中的 RestartClassLoader 会导致 MyBatis Plus 中的枚举自动映射失败,因为类加载器的不同从而在 MyBatis 的 TypeHasnlerRegistry 的 TYPE_HANDLER_MAP 集合中找不到对应的枚举类型(存在这个枚举类型,只不过是用 AppClassLoader 加载的,不同的加载器导致类型不同) |
| MyBatis Plus 和JPA 分页有些不同,前者从 1 开始计页数,后者则是从 0 开始。 |
| |