MyBatisPlus
MyBatis-Plus (opens new window)(簡稱 MP)是一個 MyBatis (opens new window) 的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。
官網地址:https://baomidou.com/
一、入門案例
1.準備表結構和數據
??準備如下的表結構和相關數據
DROP TABLE IF EXISTS user;CREATE TABLE user
(id BIGINT(20) NOT NULL COMMENT '主鍵ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年齡',email VARCHAR(50) NULL DEFAULT NULL COMMENT '郵箱',PRIMARY KEY (id)
);
插入對應的相關數據
DELETE FROM user;INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
2. 創建項目
??創建一個SpringBoot項目,然后引入相關的依賴,首先是父依賴
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.6</version><relativePath/> <!-- lookup parent from repository --></parent>
具體的其他的依賴
<!-- spring-boot-starter-web 的依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 引入MyBatisPlus的依賴 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!-- 數據庫使用MySQL數據庫 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- 數據庫連接池 Druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.14</version></dependency><!-- lombok依賴 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
3.配置信息
??然后我們需要在application.properties中配置數據源的相關信息
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mp?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
然后我們需要在SpringBoot項目的啟動類上配置Mapper接口的掃描路徑
4.添加User實體
??添加user的實體類
@ToString
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
5.創建Mapper接口
??在MyBatisPlus中的Mapper接口需要繼承BaseMapper.
/*** MyBatisPlus中的Mapper接口繼承自BaseMapper*/
public interface UserMapper extends BaseMapper<User> {
}
6.測試操作
??然后來完成對User表中數據的查詢操作
@SpringBootTest
class MpDemo01ApplicationTests {@Autowiredprivate UserMapper userMapper;@Testvoid queryUser() {List<User> users = userMapper.selectList(null);for (User user : users) {System.out.println(user);}}}
7.日志輸出
??為了便于學習我們可以指定日志的實現StdOutImpl來處理
# 指定日志輸出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
然后操作數據庫的時候就可以看到對應的日志信息了:
二、CRUD操作
1.插入用戶
??先來看看插入用戶的操作,在MyBatisPlus中給我們提供一個insert()方法來實現。
/*** 添加用戶信息*/@Testvoid addUser() {User user = new User(null, "zs", 18, "123@qq.com");int i = userMapper.insert(user);System.out.println("i = " + i);}
插入成功后生成的id是一長串數字:
注意:在MyBatisPlus中插入數據的時候,如果id為空,默認會通過雪花算法來生成id
2.更新用戶
??然后來看看MyBatisPlus中的更新操作。
/*** 更新用戶信息*/@Testvoid updateUser() {User user = new User(6l, "zs", 20, "123@qq.com");int i = userMapper.updateById(user);}
3.刪除用戶
??刪除用戶的方法在MyBatisPLUS中提供的有多個
3.1 根據id刪除
@Testvoid deleteUser() {User user = new User(6l, "zs", 20, "123@qq.com");userMapper.deleteById(6l);}
3.2 批量刪除
??MyBatisPlus中也支持批量刪除的操作
/*** 批量刪除*/@Testvoid deleteBathUser() {int i = userMapper.deleteBatchIds(Arrays.asList(1l, 2l, 3l, 4l));System.out.println("受影響的行數:" + i);}
3.3 通過Map刪除
根據 columnMap 條件,刪除記錄
/*** 根據 columnMap 條件,刪除記錄*/@Testvoid deleteMapUser() {Map<String,Object> map = new HashMap<>();map.put("age",18);map.put("name","tom");int i = userMapper.deleteByMap(map);System.out.println("受影響的行數:" + i);}
4.查詢操作
4.1 根據id查詢
??首先我們可以根據id來查詢單條記錄
@Testvoid queryUserById() {User user = userMapper.selectById(1l);System.out.println(user);}
4.2 根據id批量查詢
??然后也可以通過類似于SQL語句中的in關鍵字來實現多id的查詢
@Testvoid queryUserByBatchId() {List<User> users = userMapper.selectBatchIds(Arrays.asList(1l, 2l, 3l));users.forEach(System.out::println);}
4.3 通過Map查詢
??也可以把需要查詢的字段條件封裝到一個Map中來查詢
@Testvoid queryUserByMap() {Map<String,Object> map = new HashMap<>();map.put("age",18);map.put("name","tom");List<User> users = userMapper.selectByMap(map);users.forEach(System.out::println);}
4.4 查詢所有數據
??也可以通過selectList方法來查詢所有的數據
/*** 查詢用戶信息*/@Testvoid queryUser() {List<User> users = userMapper.selectList(null);for (User user : users) {System.out.println(user);}}
當然在selectList中需要我們傳遞進去一個Wrapper對象,這個是一個條件構造器,這個在后面會詳細的講解。
三、CRUD接口
官網地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3
官網說明:
- 通用 Service CRUD 封裝IService(opens new window)接口,進一步封裝 CRUD 采用 get 查詢單行 remove 刪除 list 查詢集合 page 分頁 前綴命名方式區分 Mapper 層避免混淆,
- 泛型 T 為任意實體對象
- 建議如果存在自定義通用 Service 方法的可能,請創建自己的 IBaseService 繼承 Mybatis-Plus 提供的基類
- 對象 Wrapper 為 條件構造器
在MyBatis-Plus中有一個接口 IService和其實現類 ServiceImpl,封裝了常見的業務層邏輯
1.Service的使用
??要使用CRUD的接口,那么我們自定義的Service接口需要繼承IService接口。
/*** User對應的Service接口* 要使用MyBatisPlus的Service完成CRUD操作,得繼承IService*/
public interface IUserService extends IService<User> {
}
對應的Service實現得繼承ServiceImpl同時指定mapper和實體對象。
/*** Service的實現類* 必須繼承ServiceImpl 并且在泛型中指定 對應的Mapper和實體對象*/
@Service
public class UserService extends ServiceImpl<UserMapper, User> implements IUserService {
}
2.查詢操作
??通過Service中提供的count方法可以查詢總的記錄數。get方法,List方法等
@Autowiredprivate IUserService userService;@Testvoid getUserCount() {long count = userService.count();System.out.println("count = " + count);}
3.批量插入
??在service中給我們提供了批量插入的方法
@Testvoid saveBatchUser() {List<User> list = new ArrayList<>();for (int i = 0; i < 10; i++) {User user = new User(null,"a"+i,10+i,"aaa@163.com");list.add(user);}// 批量插入userService.saveBatch(list);// batchSize:50// userService.saveBatch(list,50);}
還有saveOrUpdate等方法,可自行應用。
四、常用注解
1.@TableName
經過以上的測試,在使用MyBatis-Plus實現基本的CRUD時,我們并沒有指定要操作的表,只是在
Mapper接口繼承BaseMapper時,設置了泛型User,而操作的表為user表
由此得出結論,MyBatis-Plus在確定操作的表時,由BaseMapper的泛型決定,即實體類型決
定,且默認操作的表名和實體類型的類名一致
如果表名和我們的實體類的名稱不一致的話,在執行相關操作的時候會拋出對應的異常,比如數據庫的表我們該為T_USER,然后執行查詢操作。
這時我們就可以通過@TableName來解決這個問題。
/*** @TableName 標識實體類對應的表名*/
@TableName("t_user")
@AllArgsConstructor
@ToString
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
??在開發的過程中,我們經常遇到以上的問題,即實體類所對應的表都有固定的前綴,例如t_或tbl_ 此時,可以使用MyBatis-Plus提供的全局配置,為實體類所對應的表名設置默認的前綴,那么就不需要在每個實體類上通過@TableName標識實體類對應的表.
# 指定日志輸出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 配置MyBatis-Plus操作表的默認前綴
mybatis-plus.global-config.db-config.table-prefix=t_
2.@TableId
??我們可以通過@TableId注解來顯示的指定哪個屬性為主鍵對應的屬性,在前面的例子中默認id就是,如果我們的主鍵字段不是id,比如uid的話,把實體user中的id改為uid,同時表結構中的id字段也修改為uid字段。我們來看看效果。執行插入操作。
??可以看到拋出了一個 Field 'uid' doesn't
的異常,這時我們可以在User實體的uid屬性上添加@TableId即可。
??@TableId中的value值在實體類中的字段和表結構的字段一致的情況下我們不用添加,但如果不一致,@TableId中的value我們需要設置表結構中的主鍵字段。
@TableId中還有一個比較重要的屬性是Type。Type是用來定義主鍵的生成策略的。以下是官網截圖
這個可以在@TableId中配置,也可以在配置文件中統一配置全局的生成策略。
當然配置主鍵自增得在表結構中的字段要設置自動增長才行
3.@TableField
??@TableField注解的作用是當實體類中的屬性和表結構中的字段名稱不一致的情況下來設置對應關系的,當然,在MyBatis-Plus中針對實體中是userName而表結構中是user_name這種情況會自動幫助我們完成駝峰命名法的轉換。
@AllArgsConstructor
@ToString
@Data
public class User {@TableId(value = "uid",type = IdType.ASSIGN_ID)private Long uid; // 表明uid就是主鍵字段對應的屬性@TableField("name") // 表結構中的name屬性和name屬性對應private String name;private Integer age;private String email;
}
4.@TableLogic
??@TableLogic是用來完成 邏輯刪除
操作的
刪除類型 | 描述 |
---|---|
邏輯刪除 | 假刪除,將對應數據中代表是否被刪除字段的狀態修改為“被刪除狀態”,<br />之后在數據庫中仍舊能看到此條數據記錄 |
物理刪除 | 真實刪除,將對應數據從數據庫中刪除,之后查詢不到此條被刪除的數據 |
效果演示:先在表中創建一個is_deleted字段
對應的在實體類中添加一個isDeleted屬性
然后我們調用刪除功能
可以看到我們調用了deleteById方法,但是真實執行的是Update方法,實現了邏輯刪除的場景。
當然也可以在屬性文件中配置全局的
# 配置邏輯刪除
mybatis-plus.global-config.db-config.logic-delete-field=is_deleted
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
五、條件構造器
??當我們需要對單表的CURD做復雜條件處理的時候我們就需要借助Wrapper接口來處理,也就是通過條件構造器來處理。
1.Wrapper接口
??Wrapper接口是條件構造的抽象類,是最頂級的類
對應的作用描述
2.QueryWrapper
??首先來看看QueryWrapper的使用,針對where后的條件封裝。
2.1 查詢條件
/*** 查詢用戶姓名中包含 o 的年齡大于20歲,且郵箱不為null的記錄*/@Testvoid queryUser() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like("name","o").gt("age",20).isNotNull("email");List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
2.2 排序條件
??QueryWrapper也可以封裝排序的條件
/*** 根據年齡升序然后根據id降序*/@Testvoid queryUser() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.orderByAsc("age").orderByDesc("uid");List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
2.3 刪除條件
??QueryWrapper也可以封裝刪除操作的條件
/*** 刪除所有年齡小于18歲的用戶*/@Testvoid deleteUser() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.le("age",18);int i = userMapper.delete(wrapper);System.out.println(i);}
2.4 組合條件
??在封裝條件的時候我們可以同時有多個條件組合,類似于 and 和 or的操作,這時QueryWrapper也能很輕松的處理。
/*** 查詢出年齡大于20并且姓名中包含的有'o'或者郵箱地址為空的記錄*/@Testvoid queryUser() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.gt("age",20).like("name","o").or() // 默認是通過and連接 顯示加上 or()方法表示or連接.isNotNull("email");List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}@Testvoid queryUser1() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.and((i)->{i.gt("age",20).like("name","o");}).or((i)->{i.isNotNull("email");});List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
2.5 查詢特定的字段
??特殊情況我們需要查詢特定的字段,這時可以通過select方法來處理
/*** 查詢出年齡大于20并且姓名中包含的有'o'或者郵箱地址為空的記錄*/@Testvoid queryUser() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.gt("age",20).like("name","o").or() // 默認是通過and連接 顯示加上 or()方法表示or連接.isNotNull("email").select("uid","name","age") // 指定特定的字段;//selectMaps()返回Map集合列表,通常配合select()使用,避免User對象中沒有被查詢到的列值為nullList<Map<String, Object>> maps = userMapper.selectMaps(wrapper);maps.forEach(System.out::println);}
2.6 實現子查詢
??單表查詢中對子查詢的需求也是有的,我們來看看如何實現。
/*** 子查詢* SELECT uid,name,age,email,is_deleted * FROM t_user * WHERE ( * uid IN (select uid from t_user where uid < 5)* )*/@Testvoid queryUser() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.inSql("uid","select uid from t_user where uid < 5");List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);maps.forEach(System.out::println);}
3.UpdateWrapper
??當我們需要組裝更新的字段數據的時候,可以通過UpdateWrapper來實現。
/*** 更新用戶Tom的age和郵箱信息* UPDATE t_user SET age=?,email=? WHERE (name = ?)*/@Testvoid updateUser() {UpdateWrapper<User> wrapper = new UpdateWrapper<>();wrapper.set("age",25).set("email","bobo@qq.com").eq("name","Tom");int update = userMapper.update(null, wrapper);System.out.println("update = " + update);}
4.動態SQL
??實際開發中,用戶的查詢條件都是動態的,我們需要根據不同的輸入條件來動態的生成對應的SQL語句,這時我們來看看在MyBatisPlus中是如何處理的。
@Testvoid queryUser1() {String name = "Tom";Integer age = null;String email = null;QueryWrapper<User> wrapper = new QueryWrapper<>();if(!StringUtils.isEmpty(name)){wrapper.eq("name",name);}if(age != null && age > 0){wrapper.eq("age",age);}if(!StringUtils.isEmpty(email)){wrapper.eq("email",email);}List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
上面的代碼是通過if來一個個判斷的,看起來代碼比較復雜,其實大家在前面看相關的API的時候會注意到都會有一個Condition參數
我們可以用這個參數來實現對應的動態SQL處理
@Testvoid queryUser2() {String name = "Tom";Integer age = null;String email = null;QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq(StringUtils.isNotBlank(name),"name",name).eq(age!=null && age > 0 ,"age" ,age).eq(StringUtils.isNotBlank(email),"email",email);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
六、分頁插件
??在MyBatisPlus中集成了分頁插件,我們不需要單獨的引入,只需要添加對應的配置類
@Configuration
@MapperScan("com.bobo.mpdemo01.mapper")
public class MyBatisPlusConfig {/*** 新的分頁插件,一緩和二緩遵循mybatis的規則,* 需要設置 MybatisConfiguration#useDeprecatedExecutor = false 避免緩存出現問題(該屬性會在舊插件移除后一同移除)*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}
然后就可以測試操作了
@Testvoid queryPage() {Page<User> page = new Page<>(1,5);Page<User> userPage = userMapper.selectPage(page, null);System.out.println("userPage.getCurrent() = " + userPage.getCurrent());System.out.println("userPage.getSize() = " + userPage.getSize());System.out.println("userPage.getTotal() = " + userPage.getTotal());System.out.println("userPage.getPages() = " + userPage.getPages());System.out.println("userPage.hasPrevious() = " + userPage.hasPrevious());System.out.println("userPage.hasNext() = " + userPage.hasNext());}
七、代碼生成器
添加依賴
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId></dependency>
快速生成:
/*** 代碼生成器*/
public class MyFastAutoGenerator {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://localhost:3306/mp?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true", "root", "123456").globalConfig(builder -> {builder.author("boge") // 設置作者//.enableSwagger() // 開啟 swagger 模式.fileOverride() // 覆蓋已生成文件.outputDir("D://MyBatisPlus"); // 指定輸出目錄}).packageConfig(builder -> {builder.parent("com.bobo.mp") // 設置父包名.moduleName("system") // 設置父包模塊名.pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 設置mapperXml生成路徑}).strategyConfig(builder -> {builder.addInclude("t_user") // 設置需要生成的表名.addTablePrefix("t_", "c_"); // 設置過濾表前綴}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默認的是Velocity引擎模板.execute();}
}