MyBatis-Plus 是一個為了簡化開發(fā)效率而生的 MyBatis 增強工具,在 MyBatis 的基礎(chǔ)上只做增強不做改變,為簡化開發(fā)、提高效率而生。其整合到 Spring Boot 項目中,可以讓開發(fā)過程更加便捷。
一、引入依賴
com.baomidou
mybatis-plus-boot-starter
3.5.5
二、在application.properties中配置日志打印
# mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
三、代碼案例
(1)實體類
package com.example.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.*;
import java.io.Serializable;
import java.util.Date;
@Data
public class User implements Serializable {
/** 主鍵 */
@TableId(type = IdType.ID_WORKER) // mybatis-plus默認(rèn)使用IdType.ID_WORKER 雪花算法 Long類型作為id,根據(jù)主鍵類型選擇IdType
private Long id;
/** 名稱 */
private String name;
/** 年齡 */
private Integer age;
/** 郵箱 */
private String email;
/** 創(chuàng)建時間 */
@TableField(fill = FieldFill.INSERT) // 插入的時候自動填充該字段的值
private Date createTime;
/** 修改時間 */
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和修改的時候自動填充該字段的值
private Date updateTime;
@TableField(fill = FieldFill.INSERT) // 插入的時候自動填充該字段的值
@Version // 樂觀鎖版本
private Integer version;
}
/**
mybatis-plus功能:
1、默認(rèn)使用雪花算法生成主鍵
2、支持字段值自動填充
*/
(2)配置類
package com.example.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan(value = "com.example.mapper")
@EnableTransactionManagement
public class MybatisPlusConfig {
/**
* 樂觀鎖插件
* @return OptimisticLockerInterceptor
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 分頁插件
* @return PaginationInterceptor
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 邏輯刪除時用到
* @return ISqlInjector
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
}
(3)元對象處理類
package com.example.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
@Component
@Slf4j
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler { // 實現(xiàn)元對象處理器接口
// 獲取當(dāng)前日期
private Date getCurrentDateTime() {
return Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
}
// 插入元對象字段填充(用于插入時對公共字段的填充)
@Override
public void insertFill(MetaObject metaObject) {
log.info("插入元對象字段填充開始...");
// this.setFieldValByName(屬性名稱,默認(rèn)值,metaObject); // 默認(rèn)值類型 對應(yīng) 實體類屬性類型
this.setFieldValByName("createTime", getCurrentDateTime(), metaObject);
this.setFieldValByName("updateTime", getCurrentDateTime(), metaObject);
this.setFieldValByName("version", 1, metaObject);
log.info("插入元對象字段填充完成...");
}
// 更新元對象字段填充(用于更新時對公共字段的填充)
@Override
public void updateFill(MetaObject metaObject) {
log.info("更新元對象字段填充開始...");
this.setFieldValByName("updateTime", getCurrentDateTime(), metaObject);
log.info("更新元對象字段填充完成...");
}
}
(4)測試類
package com.example;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.User;
import com.example.mapper.MybatisPlusMapper;
import com.example.util.IdWorker;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisPlusTest {
// 逆向生成的類
@Autowired
private MybatisPlusMapper mybatisPlusMapper;
// 網(wǎng)上能找到該類
@Autowired
private IdWorker idWorker;
@Test
public void insertTest() {
User user = new User();
user.setId(idWorker.nextId());
user.setName("xxxxx");
user.setAge(18);
user.setEmail("xxx@qq.com");
int result = mybatisPlusMapper.insert(user);
System.out.println("result = " + result);
System.out.println("id = " + user.getId());
System.out.println(user);
}
@Test
public void updateTest() {
// 需要先查出該對象
User user = mybatisPlusMapper.selectById(1449705947919306752L);
user.setName("小哆啦");
user.setUpdateTime(null);
// 再進(jìn)行修改操作才會對版本號version進(jìn)行修改
mybatisPlusMapper.updateById(user);
}
@Test
public void selectTest() {
System.out.println("------------111------------");
User user = mybatisPlusMapper.selectById(1449705947919306752L);
Optional.ofNullable(user).ifPresent(System.out::println);
System.out.println("------------222------------");
List users = mybatisPlusMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L, 4L, 666L));
users.forEach(System.out::println);
Map map = new HashMap<>();
map.put("age", 18);
System.out.println("------------333------------");
List users2 = mybatisPlusMapper.selectByMap(map);
users2.forEach(System.out::println);
QueryWrapper userWrapper = new QueryWrapper<>();
userWrapper.eq("name", "Jack");
userWrapper.isNull("create_time"); // column 這里填的是數(shù)據(jù)庫中的字段名稱
System.out.println("------------444------------");
List users3 = mybatisPlusMapper.selectList(userWrapper);
users3.forEach(System.out::println);
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.isNull("create_time");
IPage userIPage = new Page<>(2, 3);
System.out.println("------------555------------");
mybatisPlusMapper.selectPage(userIPage, wrapper);
userIPage.getRecords().forEach(System.out::println);
System.out.println("當(dāng)前頁:" + userIPage.getCurrent());
System.out.println("每頁數(shù)據(jù)條數(shù):" + userIPage.getSize());
System.out.println("總頁數(shù):" + userIPage.getPages());
System.out.println("總數(shù)據(jù)條數(shù):" + userIPage.getTotal());
}
@Test
public void deleteTest() {
// 可以是直接刪除一條數(shù)據(jù),也可以是邏輯刪除,需要看具體配置
mybatisPlusMapper.deleteById(1449654351730593792L);
}
}
/**
1、使用樂觀鎖需要修改版本號,引入插件后,每一次修改數(shù)據(jù)版本號都會加1(需要先將數(shù)據(jù)查出來版本號才會更改)
2、使用Mybatis-plus進(jìn)行分頁查詢時,需要將"分頁插件"注入容器當(dāng)中
3、刪除時比較特殊,請往下看
*/
四、使用樂觀鎖
1、樂觀鎖需要版本號,在數(shù)據(jù)庫中添加 "版本號" 字段;
2、在實體類中添加 "版本號" 屬性,并加上 "@version" 注解;
3、將OptimisticLockerInterceptor類注入容器中,該類會對版本號version進(jìn)行自增操作;
4、代碼實際操作,需要先查出該對象,再進(jìn)行修改操作才會對版本號進(jìn)行修改(見上述測試類中的updateTest()方法)。
五、字段值自動填充
1、在字段上添加相應(yīng)注解,如:
@TableField(fill = FieldFill.INSERT) // 插入的時候自動填充該字段的值
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和修改的時候自動填充該字段的值
2、自定義一個類,實現(xiàn)元對象處理器接口"MetaObjectHandler",重寫"insertFill"和"updateFill"方法,并將該類注入容器中
六、物理刪除和邏輯刪除
// 物理刪除:真實刪除,將對應(yīng)數(shù)據(jù)從數(shù)據(jù)庫中刪除,之后查詢不到此條被刪除數(shù)據(jù)
// 邏輯刪除:假刪除,將對應(yīng)數(shù)據(jù)中代表是否被刪除字段狀態(tài)修改為“被刪除狀態(tài)”,之后在數(shù)據(jù)庫中仍舊能看到此條數(shù)據(jù)記錄
@Test
public void deleteTest() {
// 可以是直接刪除一條數(shù)據(jù),也可以是邏輯刪除,需要看具體配置
mybatisPlusMapper.deleteById(1449654351730593792L);
}
1、如上代碼,當(dāng)不做任何配置時,會直接進(jìn)行"物理刪除";
2、若要實現(xiàn)"邏輯刪除",需如下配置:
(1)在數(shù)據(jù)庫中添加一個字段"deleted";
(2)在實體類中添加一個屬性"deleted",并添加相應(yīng)注解;
// value - 刪除前的狀態(tài),delval - 刪除后的狀態(tài)
// 表示邏輯刪除,加上該注解后,刪除語句就會變成更新語句
@TableLogic(value = "0", delval = "1")
// @TableLogic
private Integer deleted;
(3)向容器中注入 "ISqlInjector" 類(邏輯刪除插件)
/**
若使用 @TableLogic 而不是 @TableLogic(value = "0", delval = "1") 時,
此時可以在application.properties文件中配置邏輯刪除的狀態(tài),也可以達(dá)到邏輯刪除的效果,如下:
# 邏輯刪除狀態(tài)
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
*/
/**
測試刪除方法后分析打印的sql語句,是一條update
注意:被刪除數(shù)據(jù)的 deleted 字段的值必須是 0,才能被選取出來執(zhí)行邏輯刪除的操作,如下日志
==> ?Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1449654351730593792(Long)
<== ? ?Updates: 0
*/
七、統(tǒng)一返回的json時間格式
默認(rèn)情況下json時間格式帶有時區(qū),并且是世界標(biāo)準(zhǔn)時間,和我們的時間差了八個小時。
可以在"application.properties"文件中進(jìn)行配置,優(yōu)化Date類型字段輸出格式。
# 返回json的全局時間格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
八、SQL 執(zhí)行性能分析插件(可選)
1、將SQL執(zhí)行性能分析插件"PerformanceInterceptor"注入容器中
@Configuration
@MapperScan(value = "com.example.mapper")
@EnableTransactionManagement
public class TeacherConfig {
/**
* SQL 執(zhí)行性能分析插件
* 開發(fā)環(huán)境使用,線上不推薦。 maxTime 指的是 sql 最大執(zhí)行時長
*/
@Bean
@Profile({"dev","test"})// 設(shè)置 dev test 環(huán)境開啟
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//ms,超過此處設(shè)置的ms則sql不執(zhí)行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
}
2、在application.properties文件中指定開發(fā)環(huán)境
spring.profiles.active=dev
/**
開發(fā)環(huán)境使用,線上不推薦。 maxTime 指的是 sql 最大執(zhí)行時長
*/
九、MyBatis-Plus代碼生成器
package com.example;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
public class CodeGenerator {
@Test
public void run() {
// 1、創(chuàng)建代碼生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("author");
gc.setOpen(false); // 生成后是否打開資源管理器
gc.setFileOverride(true); // 重新生成時文件是否覆蓋
gc.setServiceName("%sService");?? ?// 去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER); // 主鍵策略
gc.setDateType(DateType.ONLY_DATE); // 定義生成的實體類中日期類型
gc.setSwagger2(true); // 開啟Swagger2模式
? ? ? ? mpg.setGlobalConfig(gc);
// 3、數(shù)據(jù)源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/database_name");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("module-xxx"); // 模塊名
pc.setParent("com.example");
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("t_user"); // 根據(jù)哪張表生成
strategy.setNaming(NamingStrategy.underline_to_camel); // 數(shù)據(jù)庫表映射到實體的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); // 生成實體時去掉表前綴
strategy.setColumnNaming(NamingStrategy.underline_to_camel); // 數(shù)據(jù)庫表字段映射到實體的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter鏈?zhǔn)讲僮?/p>
strategy.setRestControllerStyle(true); // restful api風(fēng)格控制器
strategy.setControllerMappingHyphenStyle(true); //url中駝峰轉(zhuǎn)連字符
? ? ? ? mpg.setStrategy(strategy);
// 6、執(zhí)行
mpg.execute();
}
}
/**
放到test包下,修改一些基本配置后,直接運行run()方法即可
*/