一、入門案例
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, '[email protected]'), (2, 'Jack', 20, '[email protected]'), (3, 'Tom', 28, '[email protected]'), (4, 'Sandy', 21, '[email protected]'), (5, 'Billie', 24, '[email protected]');
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=123456 spring.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 { @Autowired private UserMapper userMapper; @Test void 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()方法來實作。
/** * 添加使用者資訊 */ @Test void addUser() { User user = new User(null, "zs", 18, "[email protected]"); int i = userMapper.insert(user); System.out.println("i = " + i); }
插入成功後生成的id是一長串數字:
注意:在MyBatisPlus中插入資料的時候,如果id為空,預設會通過雪花算法來生成id
2.更新使用者
然後來看看MyBatisPlus中的更新操作。
/** * 更新使用者資訊 */ @Test void updateUser() { User user = new User(6l, "zs", 20, "[email protected]"); int i = userMapper.updateById(user); }
3.删除使用者
删除使用者的方法在MyBatisPLUS中提供的有多個
3.1 根據id删除
@Test void deleteUser() { User user = new User(6l, "zs", 20, "[email protected]"); userMapper.deleteById(6l); }
3.2 批量删除
MyBatisPlus中也支援批量删除的操作
/** * 批量删除 */ @Test void deleteBathUser() { int i = userMapper.deleteBatchIds(Arrays.asList(1l, 2l, 3l, 4l)); System.out.println("受影響的行數:" + i); }
3.3 通過Map删除
根據 columnMap 條件,删除記錄
/** * 根據 columnMap 條件,删除記錄 */ @Test void 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來查詢單條記錄
@Test void queryUserById() { User user = userMapper.selectById(1l); System.out.println(user); }
4.2 根據id批量查詢
然後也可以通過類似于SQL語句中的in關鍵字來實作多id的查詢
@Test void queryUserByBatchId() { List<User> users = userMapper.selectBatchIds(Arrays.asList(1l, 2l, 3l)); users.forEach(System.out::println); }
4.3 通過Map查詢
也可以把需要查詢的字段條件封裝到一個Map中來查詢
@Test void 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方法來查詢所有的資料
/** * 查詢使用者資訊 */ @Test void queryUser() { List<User> users = userMapper.selectList(null); for (User user : users) { System.out.println(user); } }
當然在selectList中需要我們傳遞進去一個Wrapper對象,這個是一個條件構造器,這個在後面會詳細的講解。
三、CRUD接口
官網位址:CRUD 接口 | MyBatis-Plus
官網說明:
- 通用 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方法等
@Autowired private IUserService userService; @Test void getUserCount() { long count = userService.count(); System.out.println("count = " + count); }
3.批量插入
在service中給我們提供了批量插入的方法
@Test void saveBatchUser() { List<User> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { User user = new User(null,"a"+i,10+i,"[email protected]"); list.add(user); } // 批量插入 userService.saveBatch(list); // batchSize:50 // userService.saveBatch(list,50); }
還有saveOrUpdate等方法,可自行應用。
四、常用注解
[email protected]
經過以上的測試,在使用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_
[email protected]
我們可以通過@TableId注解來顯示的指定哪個屬性為主鍵對應的屬性,在前面的例子中預設id就是,如果我們的主鍵字段不是id,比如uid的話,把實體user中的id改為uid,同時表結構中的id字段也修改為uid字段。我們來看看效果。執行插入操作。
可以看到抛出了一個
Field 'uid' doesn't
的異常,這時我們可以在User實體的uid屬性上添加@TableId即可。
@TableId中的value值在實體類中的字段和表結構的字段一緻的情況下我們不用添加,但如果不一緻,@TableId中的value我們需要設定表結構中的主鍵字段。
@TableId中還有一個比較重要的屬性是Type。Type是用來定義主鍵的生成政策的。以下是官網截圖
這個可以在@TableId中配置,也可以在配置檔案中統一配置全局的生成政策。
當然配置主鍵自增得在表結構中的字段要設定自動增長才行
[email protected]
@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; }
[email protected]
@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的記錄 */ @Test void 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降序 */ @Test void 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歲的使用者 */ @Test void 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'或者郵箱位址為空的記錄 */ @Test void 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); } @Test void 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'或者郵箱位址為空的記錄 */ @Test void 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對象中沒有被查詢到的列值為null List<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) * ) */ @Test void 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 = ?) */ @Test void updateUser() { UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.set("age",25) .set("email","[email protected]") .eq("name","Tom"); int update = userMapper.update(null, wrapper); System.out.println("update = " + update); }
4.動态SQL
實際開發中,使用者的查詢條件都是動态的,我們需要根據不同的輸入條件來動态的生成對應的SQL語句,這時我們來看看在MyBatisPlus中是如何處理的。
@Test void 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處理
@Test void 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 避免緩存出現問題(該屬性會在舊插件移除後一同移除) */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
然後就可以測試操作了
@Test void 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(); } }