MyBatisPlus之CRUD接口—IService與BaseMapper
- 一、BaseMapper與IService的關系
- 二、BaseMapper核心方法詳解
- 2.1 新增操作(Insert)
- 2.2 查詢操作(Select)
- 2.3 更新操作(Update)
- 2.4 刪除操作(Delete)
- 三、IService核心方法詳解
- 3.1 新增操作(Save)
- 3.2 查詢操作(Get/List)
- 3.3 更新操作(Update)
- 3.4 刪除操作(Remove)
- 3.5 其他實用方法
- 四、BaseMapper與IService的選擇策略
- 五、實戰技巧與避坑指南
- 5.1 Wrapper的靈活使用
- 5.2 批量操作的性能優化
- 5.3 避免N+1查詢問題
- 5.4 邏輯刪除與查詢的注意事項
MyBatisPlus(MP)的核心優勢之一是提供了開箱即用的CRUD接口,通過BaseMapper
(DAO層)和IService
(Service層)封裝了單表操作的常用方法,無需編寫SQL即可完成大部分數據庫操作。
一、BaseMapper與IService的關系
在MyBatisPlus中,BaseMapper
和IService
是實現CRUD操作的兩大核心接口,二者分工明確又相互配合:
- BaseMapper:位于DAO層,直接與數據庫交互,提供基礎的CRUD方法(如
insert
、selectById
),需由用戶自定義的Mapper接口繼承。 - IService:位于Service層,基于
BaseMapper
封裝了更豐富的業務方法(如批量操作、分頁查詢),并提供事務支持,需由用戶自定義的Service接口繼承。
調用關系:IService
的實現類(如ServiceImpl
)會注入BaseMapper
實例,通過調用BaseMapper
的方法完成數據庫操作,同時添加業務邏輯和事務控制。
使用建議:
- 簡單查詢直接使用
BaseMapper
; - 復雜業務(如批量操作、事務管理)優先使用
IService
; - 自定義SQL通過
BaseMapper
的方法擴展。
二、BaseMapper核心方法詳解
BaseMapper<T>
是MP的基礎接口,泛型T
為實體類類型。所有自定義Mapper接口只需繼承它,即可獲得17個基礎CRUD方法。
2.1 新增操作(Insert)
方法簽名 | 功能描述 | 示例 |
---|---|---|
int insert(T entity) | 插入一條記錄 | userMapper.insert(user) |
說明:
- 插入時會根據實體類的注解(如
@TableId
)自動處理主鍵生成; - 若字段未設置值,會插入
null
(除非配置了自動填充); - 返回值為受影響的行數(成功插入返回1)。
示例:
User user = new User();
user.setUsername("張三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
int rows = userMapper.insert(user); // 插入成功后,user.getId()會自動回填主鍵
System.out.println("插入行數:" + rows + ",生成ID:" + user.getId());
2.2 查詢操作(Select)
BaseMapper
提供了7種查詢方法,覆蓋單條查詢、批量查詢、條件查詢等場景:
方法簽名 | 功能描述 | 適用場景 |
---|---|---|
T selectById(Serializable id) | 根據ID查詢 | 已知主鍵的單條查詢 |
List<T> selectBatchIds(Collection<?> ids) | 批量查詢(根據ID集合) | 批量獲取多條記錄 |
List<T> selectByMap(Map<String, Object> map) | 根據Map條件查詢 | 簡單條件查詢(鍵為字段名) |
T selectOne(@Param("ew") Wrapper<T> queryWrapper) | 根據條件查詢單條 | 確保結果唯一的查詢(如唯一索引) |
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper) | 條件查詢總數 | 統計符合條件的記錄數 |
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper) | 條件查詢列表 | 復雜條件的多條查詢 |
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper) | 條件查詢(返回Map) | 只需要部分字段,無需實體類 |
示例1:根據ID查詢
User user = userMapper.selectById(1L); // ID為1的用戶
示例2:批量查詢
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.selectBatchIds(ids); // 查詢ID為1、2、3的用戶
示例3:條件查詢(使用QueryWrapper)
// 查詢年齡≥20且用戶名包含"張"的用戶
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age", 20) // 年齡≥20.like("username", "張"); // 用戶名含"張"
List<User> users = userMapper.selectList(queryWrapper);
示例4:查詢總數
// 統計年齡<18的用戶數量
Integer count = userMapper.selectCount(new QueryWrapper<User>().lt("age", 18)
);
2.3 更新操作(Update)
方法簽名 | 功能描述 | 適用場景 |
---|---|---|
int updateById(@Param("et") T entity) | 根據ID更新 | 已知主鍵,更新部分字段 |
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper) | 根據條件更新 | 按條件批量更新 |
示例1:根據ID更新
User user = new User();
user.setId(1L); // 必須設置ID
user.setAge(21); // 只更新年齡
int rows = userMapper.updateById(user); // SQL:UPDATE user SET age=21 WHERE id=1
示例2:條件更新
// 將所有年齡<18的用戶狀態改為"禁用"
User user = new User();
user.setStatus(StatusEnum.DISABLE);UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.lt("age", 18); // 條件:年齡<18int rows = userMapper.update(user, updateWrapper);
// SQL:UPDATE user SET status=0 WHERE age < 18
2.4 刪除操作(Delete)
方法簽名 | 功能描述 | 適用場景 |
---|---|---|
int deleteById(Serializable id) | 根據ID刪除 | 刪除單條記錄 |
int deleteBatchIds(Collection<?> ids) | 批量刪除(根據ID集合) | 批量刪除多條記錄 |
int deleteByMap(Map<String, Object> map) | 根據Map條件刪除 | 簡單條件的批量刪除 |
int delete(@Param("ew") Wrapper<T> queryWrapper) | 根據條件刪除 | 復雜條件的批量刪除 |
示例1:根據ID刪除
int rows = userMapper.deleteById(1L); // 刪除ID為1的用戶
示例2:條件刪除
// 刪除郵箱為空的用戶
int rows = userMapper.delete(new QueryWrapper<User>().isNull("email")
);
三、IService核心方法詳解
IService<T>
是Service層的接口,基于BaseMapper
擴展了更豐富的方法,尤其適合復雜業務場景。自定義Service接口需繼承IService
,實現類需繼承ServiceImpl<Mapper, T>
。
3.1 新增操作(Save)
方法簽名 | 功能描述 | 與BaseMapper的區別 |
---|---|---|
boolean save(T entity) | 插入一條記錄 | 返回boolean(成功/失敗),BaseMapper返回int |
boolean saveBatch(Collection<T> entityList) | 批量插入 | 內部默認分批插入(默認1000條/批) |
boolean saveBatch(Collection<T> entityList, int batchSize) | 自定義批次大小的批量插入 | 可指定每批插入數量 |
示例1:單條插入
User user = new User();
user.setUsername("李四");
boolean success = userService.save(user); // 成功返回true
示例2:批量插入
List<User> userList = new ArrayList<>();
for (int i = 0; i < 100; i++) {User user = new User();user.setUsername("用戶" + i);user.setAge(18 + i % 20);userList.add(user);
}
// 批量插入(默認每批1000條)
boolean success = userService.saveBatch(userList);
// 自定義每批50條
// userService.saveBatch(userList, 50);
3.2 查詢操作(Get/List)
IService
的查詢方法在BaseMapper
基礎上增加了分頁查詢和鏈式調用支持:
方法簽名 | 功能描述 | 特色功能 |
---|---|---|
T getById(Serializable id) | 根據ID查詢 | 同selectById ,返回null 時無異常 |
List<T> listByIds(Collection<?> ids) | 批量查詢(ID集合) | 同selectBatchIds |
List<T> list(Wrapper<T> queryWrapper) | 條件查詢列表 | 同selectList |
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper) | 分頁查詢 | 支持分頁插件,返回分頁對象 |
long count(Wrapper<T> queryWrapper) | 條件查詢總數 | 同selectCount ,返回long類型 |
boolean exists(Wrapper<T> queryWrapper) | 判斷是否存在符合條件的記錄 | 簡化count > 0 的判斷 |
示例1:分頁查詢
// 分頁查詢:第2頁,每頁10條,條件:年齡≥20
Page<User> page = new Page<>(2, 10); // 頁碼從1開始
IPage<User> userPage = userService.page(page, new QueryWrapper<User>().ge("age", 20)
);List<User> records = userPage.getRecords(); // 當前頁數據
long total = userPage.getTotal(); // 總條數
long pages = userPage.getPages(); // 總頁數
示例2:判斷記錄是否存在
// 判斷是否存在用戶名=張三的用戶
boolean exists = userService.exists(new QueryWrapper<User>().eq("username", "張三")
);
3.3 更新操作(Update)
方法簽名 | 功能描述 | 特色功能 |
---|---|---|
boolean updateById(T entity) | 根據ID更新 | 同updateById ,返回boolean |
boolean update(Wrapper<T> updateWrapper) | 根據條件更新(無實體類) | 直接通過Wrapper設置更新字段 |
boolean update(T entity, Wrapper<T> updateWrapper) | 根據條件更新 | 同BaseMapper.update |
boolean updateBatchById(Collection<T> entityList) | 批量更新(根據ID) | 批量更新多條記錄 |
boolean updateBatchById(Collection<T> entityList, int batchSize) | 自定義批次的批量更新 | 可指定每批更新數量 |
示例1:通過Wrapper直接更新
// 無需實體類,直接設置更新字段
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("username", "張三") // 條件.set("age", 22) // 更新字段.set("email", "new@example.com");
boolean success = userService.update(updateWrapper);
示例2:批量更新
List<User> userList = new ArrayList<>();
// 假設userList中包含多個設置了ID和待更新字段的User對象
boolean success = userService.updateBatchById(userList);
3.4 刪除操作(Remove)
方法簽名 | 功能描述 | 與BaseMapper的區別 |
---|---|---|
boolean removeById(Serializable id) | 根據ID刪除 | 返回boolean,BaseMapper返回int |
boolean removeByIds(Collection<?> ids) | 批量刪除(ID集合) | 同deleteBatchIds ,返回boolean |
boolean remove(Wrapper<T> queryWrapper) | 根據條件刪除 | 同delete ,返回boolean |
boolean removeByMap(Map<String, Object> map) | 根據Map條件刪除 | 同deleteByMap ,返回boolean |
示例:
// 根據條件刪除
boolean success = userService.remove(new QueryWrapper<User>().eq("status", StatusEnum.DISABLE)
);
3.5 其他實用方法
方法簽名 | 功能描述 | 示例 |
---|---|---|
T getOne(Wrapper<T> queryWrapper, boolean throwEx) | 查詢單條,支持是否拋異常 | getOne(wrapper, true) 結果不唯一時拋異常 |
List<T> list(IPage<T> page, Wrapper<T> queryWrapper) | 分頁查詢(返回List) | 只獲取分頁數據,忽略總條數 |
boolean saveOrUpdate(T entity) | 新增或更新(根據ID判斷) | 有ID則更新,無ID則插入 |
boolean saveOrUpdateBatch(Collection<T> entityList) | 批量新增或更新 | 批量處理新增/更新 |
示例:saveOrUpdate(新增或更新)
User user1 = new User();
user1.setId(1L); // 有ID → 更新
user1.setAge(23);User user2 = new User();
user2.setUsername("新用戶"); // 無ID → 新增userService.saveOrUpdate(user1);
userService.saveOrUpdate(user2);
四、BaseMapper與IService的選擇策略
場景 | 推薦使用 | 理由 |
---|---|---|
簡單CRUD操作(單表) | 優先IService | 方法返回boolean,更符合業務邏輯判斷;支持批量操作 |
復雜查詢(多條件) | IService + Wrapper | 鏈式調用更簡潔,支持分頁 |
事務管理 | IService | Service層天然適合控制事務(@Transactional ) |
自定義SQL | BaseMapper | 需在Mapper接口中定義方法,通過@Select 等注解實現 |
批量操作(1000+條) | IService的批量方法 | 內置分批處理,避免SQL過長導致性能問題 |
分布式事務 | IService | 結合@Transactional(rollbackFor = Exception.class) 確保事務一致性 |
五、實戰技巧與避坑指南
5.1 Wrapper的靈活使用
-
LambdaQueryWrapper:避免硬編碼字段名,推薦使用:
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>(); lambdaWrapper.ge(User::getAge, 20) // 引用方法,類型安全.like(User::getUsername, "張");
-
條件判斷:通過
if
動態組裝條件:LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); if (minAge != null) {wrapper.ge(User::getAge, minAge); } if (keyword != null) {wrapper.like(User::getUsername, keyword).or().like(User::getEmail, keyword); }
5.2 批量操作的性能優化
- 設置合理的批次大小:
saveBatch
默認每批1000條,可根據數據庫性能調整(如MySQL建議500-1000條/批); - 關閉批量操作的自動填充:若無需更新時間等字段,可通過全局配置關閉,提升性能;
- 使用
INSERT INTO ... VALUES (...), (...)
語法:MP的批量插入默認使用此語法,比循環單條插入效率高10倍以上。
5.3 避免N+1查詢問題
當查詢列表后需要根據關聯ID查詢其他表時,容易出現N+1問題(1次查列表,N次查詳情)。解決方案:
- 手動編寫聯表查詢SQL(通過
BaseMapper
擴展); - 使用MP的
@TableField
關聯查詢(適合簡單關聯); - 結合
PageHelper
等插件實現分頁聯表查詢。
5.4 邏輯刪除與查詢的注意事項
- 邏輯刪除后,
BaseMapper
和IService
的查詢方法會自動過濾已刪除數據(添加is_deleted = 0
條件); - 自定義SQL需手動添加邏輯刪除。
若這篇內容幫到你,動動手指支持下!關注不迷路,干貨持續輸出!
ヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノ