認識MyBatis的好兄弟 MyBatis – Plus
- 2019 年 10 月 4 日
- 筆記
快速搭建
我們先通過一個簡單的Demo 來認識一下 MyBatis – Plus 的搭建和用法:
現在資料庫中創建一張 User
表,表結構和數據如下
id |
name |
age |
|
---|---|---|---|
1 |
lx |
21 |
|
2 |
cxuan |
20 |
|
3 |
liux |
28 |
|
4 |
xiaona |
21 |
|
5 |
lxuan |
24 |
對應的建表語句和SQL語句如下
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) ); INSERT INTO `user` VALUES (1, 'lx', 21, '[email protected]'); INSERT INTO `user` VALUES (2, 'cxuan', 20, '[email protected]'); INSERT INTO `user` VALUES (3, 'liux', 28, '[email protected]'); INSERT INTO `user` VALUES (4, 'xiaona', 21, '[email protected]'); INSERT INTO `user` VALUES (5, 'lxuan', 24, '[email protected]');
使用 IDEA 新建一個 SpringBoot 項目,構建完成後,在 pom.xml
添加如下依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> </dependencies>
新建 application.yml
,在yml 文件上添加如下資料庫驅動資訊
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis username: root password: 123456
在主方法上添加 @MapperScan
註解,掃描mapper 包下面的內容
@SpringBootApplication @MapperScan("com.mybatis.plus.mapper") public class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }
新建一個 User
類,使用 @Data lombok 中的註解,如下
@Data public class User { private long id; private String name; private Integer age; private String email; }
然後新建一個介面,這個介面實現 BaseMapper
介面
public interface UserMapper extends BaseMapper<User> {}
使用單元測試進行測試:
@RunWith(SpringRunner.class) @SpringBootTest public class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test public void contextLoads() { System.out.println("----- selectAll method test -----"); List<User> userList = userMapper.selectList(null); Assert.assertEquals(5,userList.size()); userList.forEach(System.out::println); } }
輸出:
User(id=1, name=lx, age=21, [email protected]) User(id=2, name=cxuan, age=20, [email protected]) User(id=3, name=liux, age=28, [email protected]) User(id=4, name=xiaona, age=21, [email protected]) User(id=5, name=lxuan, age=24, [email protected])
通過以上幾個簡單的步驟,我們就實現了 User 表的 查詢 功能,你沒有看到一條 SQL 語句和 映射的編寫。
從以上步驟中,我們可以看到集成MyBatis-Plus
非常的簡單,只需要引入 starter 工程,並配置 mapper 掃描路徑即可。
特性
- 無侵入: 只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
- 損耗小: 啟動即會自動注入基本 CURD,性能基本無損耗,直接面向對象操作
- 強大的 CRUD 操作: 內置通用 Mapper、通用 Service,僅僅通過少量配置即可實現單表大部分 CRUD 操作,更有強大的條件構造器,滿足各類使用需求
- 支援 Lambda 形式調用: 通過 Lambda 表達式,方便的編寫各類查詢條件,無需再擔心欄位寫錯
- 支援主鍵自動生成: 支援多達 4 種主鍵策略(內含分散式唯一 ID 生成器 – Sequence),可自由配置,完美解決主鍵問題
- 支援 ActiveRecord 模式: 支援 ActiveRecord 形式調用,實體類只需繼承 Model 類即可進行強大的 CRUD 操作
- 支援自定義全局通用操作: 支援全局通用方法注入( Write once, use anywhere )
- 內置程式碼生成器: 採用程式碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層程式碼,支援模板引擎,更有超多自定義配置等您來使用
- 內置分頁插件: 基於 MyBatis 物理分頁,開發者無需關心具體操作,配置好插件之後,寫分頁等同於普通 List 查詢
- 分頁插件支援多種資料庫: 支援 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多種資料庫
- 內置性能分析插件: 可輸出 Sql 語句以及其執行時間,建議開發測試時啟用該功能,能快速揪出慢查詢
- 內置全局攔截插件: 提供全表 delete 、 update 操作智慧分析阻斷,也可自定義攔截規則,預防誤操作
MyBatis 核心功能
Mapper CRUD 介面
「說明:
- 通用 CRUD 封裝BaseMapper[1]介面,為
Mybatis-Plus
啟動時自動解析實體表關係映射轉換為Mybatis
內部對象注入容器 - 泛型
T
為任意實體對象 - 參數
Serializable
為任意類型主鍵Mybatis-Plus
不推薦使用複合主鍵約定,每一張表都有自己的唯一id
主鍵 - 對象
Wrapper
為 條件構造器[2]
看下其基本使用
Insert
insert 只有一種用法,直接插入對象的實體
/** * 插入一條記錄 * @param entity 實體對象 * @return 插入成功記錄數 */ int insert(T entity);
驗證
我們先為 User
實體類添加 @AllArgsConstructor
註解,如果 Lombok 不熟的同學請移步至 這些極簡的註解你都清楚嗎 這篇文章一探究竟。
下面是測試所用的程式碼
@Test public void insertUserEntity(){ userMapper.insert(new User(6,"cxuanxuan",25,"[email protected]")); }
「注意 Junit 中測試的 insert 插入方法是沒有返回值的,不然會報錯,所以返回類型是void 。但其實 insert 方法是有返回值的,返回的是插入成功的記錄數。
Delete
delete 有四種用法,分別是根據 id 刪除、通過某個表欄位刪除、通過實體類刪除和批量刪除,比我們最初只是通過 id 刪除功能強大很多。
/** * 根據 ID 刪除 * @param id 主鍵ID * @return 刪除成功記錄數 */ int deleteById(Serializable id); /** * 根據 columnMap 條件,刪除記錄 * @param columnMap 表欄位 map 對象 * @return 刪除成功記錄數 */ int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** * 根據 entity 條件,刪除記錄 * @param wrapper 實體對象封裝操作類(可以為 null) * @return 刪除成功記錄數 */ int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); /** * 刪除(根據ID 批量刪除) * @param idList 主鍵ID列表(不能為 null 以及 empty) * @return 刪除成功記錄數 */ int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
驗證
驗證示例如下:
// 根據某個特定的 id 進行刪除。 @Test public void deleteUserById(){ userMapper.deleteById(6); } // deleteByMap 參數是 Map<String, Object> 類型 // 第一個范型是欄位名稱,第二個范型是欄位對應的值 @Test public void deleteByColumnMap(){ Map<String,Object> columnMap = new HashMap<>(); columnMap.put("id",4); userMapper.deleteByMap(columnMap); } // 使用 QueryWrapper 進行條件篩選 @Test public void deleteEntity(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); // 刪除 name 不為 null ,並且 age 大於等於 28的記錄 queryWrapper.isNotNull("name").ge("age",28); userMapper.delete(queryWrapper); } // 根據 Collection 集合進行刪除,也可以是 Collection 的子類 @Test public void deleteBatch(){ List list = new ArrayList(); // 批量刪除 1 和 2 的數據 list.add(1); list.add(2); userMapper.deleteBatchIds(list); }
上面是 Delete 方法的測試例子,其中 **deleteByColumnMap 和 deleteEntity ** 方法如果不傳任何值,默認刪除表中所有的數據。deleteBatch
如果傳遞 null 的話,測試會報錯,無法通過單元測試。
Update
Update 更新方法只有兩種形式,一種是直接根據 id 修改實體屬性的值,一種是直接更新實體類
/** * 根據 ID 修改 * @param entity 實體對象 * @return 修改成功記錄數 */ int updateById(@Param(Constants.ENTITY) T entity); /** * 根據 whereEntity 條件,更新記錄 * @param entity 實體對象 (set 條件值,可為 null) * @param updateWrapper 實體對象封裝操作類(可以為 null,裡面的 entity 用於生成 where 語句) * @return 修改成功記錄數 */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
驗證
// 根據id更新User表 @Test public void updateUserById(){ userMapper.updateById(new User(5,"cxuaner",25,"[email protected]")); } // 更新 User 實體類資訊 @Test public void updateUserEntity(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper(); updateWrapper.eq("age",28); userMapper.update(new User(3,"cxuan",22,"[email protected]"),updateWrapper); }
- 第一條 updateUserById 語句是根據 id 進行更新,傳入一個實體類的意思是更新 id = 5的這條記錄。 傳入 null 會報錯,如果沒有 id = 5的這條記錄,測試會成功,但是不會更新任何記錄
- 第二條 updateUserEntity 語句是根據實體類的條件進行更新,
UpdateWrapper
是一個條件構造器,測試的是 age 為 28 的這條記錄進行更新。 條件構造器後面會進行更加詳細的介紹。
Select
Select 用法比較多,現在來一起看一下Select 有哪些用法
/** * 根據 ID 查詢 * @param id 主鍵ID * @return 實體 */ T selectById(Serializable id); /** * 查詢(根據ID 批量查詢) * @param idList 主鍵ID列表(不能為 null 以及 empty) * @return 實體集合 */ List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); /** * 查詢(根據 columnMap 條件) * @param columnMap 表欄位 map 對象 * @return 實體集合 */ List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** * 根據 entity 條件,查詢一條記錄 * @param queryWrapper 實體對象 * @return 實體 */ T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根據 Wrapper 條件,查詢總記錄數 * @param queryWrapper 實體對象 * @return 滿足條件記錄數 */ Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根據 entity 條件,查詢全部記錄 * @param queryWrapper 實體對象封裝操作類(可以為 null) * @return 實體集合 */ List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根據 Wrapper 條件,查詢全部記錄 * @param queryWrapper 實體對象封裝操作類(可以為 null) * @return 欄位映射對象 Map 集合 */ List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根據 Wrapper 條件,查詢全部記錄 * 注意:只返回第一個欄位的值 * @param queryWrapper 實體對象封裝操作類(可以為 null) * @return 欄位映射對象集合 */ List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根據 entity 條件,查詢全部記錄(並翻頁) * @param page 分頁查詢條件(可以為 RowBounds.DEFAULT) * @param queryWrapper 實體對象封裝操作類(可以為 null) * @return 實體分頁對象 */ IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根據 Wrapper 條件,查詢全部記錄(並翻頁) * @param page 分頁查詢條件 * @param queryWrapper 實體對象封裝操作類 * @return 欄位映射對象 Map 分頁對象 */ IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
驗證
下面就對上面的示例方法進行驗證
/** * 查詢id = 1 的一條記錄 */ @Test public void selectById(){ User user = userMapper.selectById(1); System.out.println("user = " + user); } /** * 批量查詢id = 1,2,3 的記錄 */ @Test public void selectUserBatchId(){ List batchIds = new ArrayList(); batchIds.add(1); batchIds.add(2); batchIds.add(3); List list = userMapper.selectBatchIds(batchIds); System.out.println("list = " + list); } /* * 查詢年齡為 21 歲的記錄,並循環遍歷輸出 */ @Test public void selectUserByMap(){ Map<String,Object> userMap = new HashMap<>(); userMap.put("age",21); List<User> users = userMapper.selectByMap(userMap); users.forEach(p-> { System.out.println(p); }); } /** * 根據 entity 條件,查詢一條id = 1 的記錄,需要手動添加 User(long id)的構造器 */ @Test public void selectOneUser(){ // 需要在 User 類上添加一個 id 的構造函數 User user = userMapper.selectOne(new QueryWrapper<>(new User(1))); System.out.println("user = " + user); } /** * 根據 Wrapper 條件,查詢年齡大於 22 的總條目數 */ @Test public void selectCountNum(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); Integer integer = userMapper.selectCount(queryWrapper); System.out.println("查詢出來的總記錄數 = " + integer); } /* * 查詢年齡大於22 的所有記錄數,返回一個List */ @Test public void selectUserList(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); List<User> users = userMapper.selectList(queryWrapper); users.forEach(p-> { System.out.println(p); }); } /* * 查詢年齡大於22 的所有記錄數,返回的是一個List<Map> */ @Test public void selectUserMaps(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper); maps.forEach(p-> { System.out.println(p); }); } /** * 查詢年齡大於22 的所有記錄數,返回的是一個List<Object> * 注意:只返回第一個欄位的值 */ @Test public void selectUserObject(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); List<Object> objects = userMapper.selectObjs(queryWrapper); objects.forEach(p-> { System.out.println(p); }); }
參考資料
[1]
BaseMapper: https://gitee.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java
[2]
條件構造器: https://mp.baomidou.com/guide/wrapper.html