SpringBoot整合MyBatis-plus
原創作品,可以轉載,但是請標註出處地址:ofollow,noindex" target="_blank">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 { // mybatis-plus分頁外掛 @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); } // 注意,這裡引數animal不能用RequstBody標註,否則接收不到引數 // @RequestBody只能用在只有一個引數模型的方法中,用於將所有請求體中攜帶的引數全部對映到這個請求引數模型中 @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 // mybatis-plus分頁外掛 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 # 邏輯已刪除值(預設為 1) logic-not-delete-value: 0 # 邏輯未刪除值(預設為 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 { //... //sql執行效率外掛(效能分析外掛) @Bean @Profile({"dev","test"})// 設定 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 { //... // mybatis-plus樂觀鎖外掛 @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 { /** * 資料庫ID自增 */ AUTO(0), /** * 該型別為未設定主鍵型別 */ NONE(1), /** * 使用者輸入ID * 該型別可以通過自己註冊自動填充外掛進行填充 */ INPUT(2), /* 以下3種類型、只有當插入物件ID 為空,才自動填充。 */ /** * 全域性唯一ID (idWorker) */ ID_WORKER(3), /** * 全域性唯一ID (UUID) */ UUID(4), /** * 字串全域性唯一ID (idWorker 的字串表示) */ 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開始。