大部分CRUD都来源这个类,对此有意义剖析,方便之后的功能开发
在 MyBatis-Plus 中,IService 是 MyBatis-Plus 提供的服务层的接口,用于定义业务层的一些通用方法。
这个接口位于 MyBatis-Plus 的核心模块中,与 BaseMapper 接口一同构成 MyBatis-Plus 的基础架构。
以下是关于 IService 类的一般概念、作用和功能:
概念 | 作用 | 功能 |
---|---|---|
类似于 BaseMapper 是数据访问层的通用接口,IService 是服务层的通用接口,提供了一组常用的业务操作方法。 | 1.提供了通用的业务层方法,包括常见的增、删、改、查等操作。 2.简化了业务层的开发,通过继承 IService,开发者可以直接使用其中定义的通用方法,而无需编写重复代码。 3.支持事务管理,可以在业务层的方法中实现事务控制。 4、与 BaseMapper 配合使用,形成数据访问层和服务层的整体解决方案,提高代码的可维护性和可扩展性。 |
1.通用的 CRUD 操作: 提供了基本的增、删、改、查方法,与 BaseMapper 中的对应方法一致。 2.事务管理: 支持事务,可以在业务层的方法上使用 @Transactional 注解实现事务控制。 3.业务层方法: 除了 CRUD 操作外,还可以在接口中定义其他业务层方法,根据具体业务需求进行扩展。 4.Lambda 表达式查询: 支持在业务层使用 Lambda 表达式进行查询,提高查询条件的类型安全性。 5.逻辑删除: 支持逻辑删除,通过标记删除而不是物理删除,提高了数据安全性。 6.乐观锁: 支持乐观锁机制,用于处理并发更新时的数据一致性。 |
示例代码:
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
// 无需手动编写业务逻辑,通过继承 IService 即可使用通用的 CRUD 方法
// 可以在接口中定义其他业务层方法
}
通过继承 IService 接口,开发者可以直接使用其中定义的通用方法,同时可以在接口中定义业务层方法,形成业务层的整体解决方案。
这样的设计可以提高代码的可维护性、可读性,同时减少了开发者对底层实现的关注,更专注于业务逻辑的实现。
对应的源码如下:
public interface IService<T> {
/**
* 默认批次提交数量
*/
int DEFAULT_BATCH_SIZE = 1000;
/**
* 插入一条记录(选择字段,策略插入)
*
* @param entity 实体对象
*/
default boolean save(T entity) {
return SqlHelper.retBool(getBaseMapper().insert(entity));
}
/**
* 插入(批量)
*
* @param entityList 实体对象集合
*/
@Transactional(rollbackFor = Exception.class)
default boolean saveBatch(Collection<T> entityList) {
return saveBatch(entityList, DEFAULT_BATCH_SIZE);
}
/**
* 插入(批量)
*
* @param entityList 实体对象集合
* @param batchSize 插入批次数量
*/
boolean saveBatch(Collection<T> entityList, int batchSize);
/**
* 批量修改插入
*
* @param entityList 实体对象集合
*/
@Transactional(rollbackFor = Exception.class)
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
}
/**
* 批量修改插入
*
* @param entityList 实体对象集合
* @param batchSize 每次的数量
*/
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
default boolean removeById(Serializable id) {
return SqlHelper.retBool(getBaseMapper().deleteById(id));
}
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
default boolean removeByMap(Map<String, Object> columnMap) {
Assert.notEmpty(columnMap, "error: columnMap must not be empty");
return SqlHelper.retBool(getBaseMapper().deleteByMap(columnMap));
}
/**
* 根据 entity 条件,删除记录
*
* @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default boolean remove(Wrapper<T> queryWrapper) {
return SqlHelper.retBool(getBaseMapper().delete(queryWrapper));
}
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表
*/
default boolean removeByIds(Collection<? extends Serializable> idList) {
if (CollectionUtils.isEmpty(idList)) {
return false;
}
return SqlHelper.retBool(getBaseMapper().deleteBatchIds(idList));
}
/**
* 根据 ID 选择修改
*
* @param entity 实体对象
*/
default boolean updateById(T entity) {
return SqlHelper.retBool(getBaseMapper().updateById(entity));
}
/**
* 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
*
* @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
*/
default boolean update(Wrapper<T> updateWrapper) {
return update(null, updateWrapper);
}
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象
* @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
*/
default boolean update(T entity, Wrapper<T> updateWrapper) {
return SqlHelper.retBool(getBaseMapper().update(entity, updateWrapper));
}
/**
* 根据ID 批量更新
*
* @param entityList 实体对象集合
*/
@Transactional(rollbackFor = Exception.class)
default boolean updateBatchById(Collection<T> entityList) {
return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
}
/**
* 根据ID 批量更新
*
* @param entityList 实体对象集合
* @param batchSize 更新批次数量
*/
boolean updateBatchById(Collection<T> entityList, int batchSize);
/**
* TableId 注解存在更新记录,否插入一条记录
*
* @param entity 实体对象
*/
boolean saveOrUpdate(T entity);
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
default T getById(Serializable id) {
return getBaseMapper().selectById(id);
}
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表
*/
default List<T> listByIds(Collection<? extends Serializable> idList) {
return getBaseMapper().selectBatchIds(idList);
}
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
default List<T> listByMap(Map<String, Object> columnMap) {
return getBaseMapper().selectByMap(columnMap);
}
/**
* 根据 Wrapper,查询一条记录
* 结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default T getOne(Wrapper<T> queryWrapper) {
return getOne(queryWrapper, true);
}
/**
* 根据 Wrapper,查询一条记录
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
* @param throwEx 有多个 result 是否抛出异常
*/
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
/**
* 根据 Wrapper,查询一条记录
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
Map<String, Object> getMap(Wrapper<T> queryWrapper);
/**
* 根据 Wrapper,查询一条记录
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
* @param mapper 转换函数
*/
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
/**
* 查询总记录数
*
* @see Wrappers#emptyWrapper()
*/
default int count() {
return count(Wrappers.emptyWrapper());
}
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default int count(Wrapper<T> queryWrapper) {
return SqlHelper.retCount(getBaseMapper().selectCount(queryWrapper));
}
/**
* 查询列表
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default List<T> list(Wrapper<T> queryWrapper) {
return getBaseMapper().selectList(queryWrapper);
}
/**
* 查询所有
*
* @see Wrappers#emptyWrapper()
*/
default List<T> list() {
return list(Wrappers.emptyWrapper());
}
/**
* 翻页查询
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
return getBaseMapper().selectPage(page, queryWrapper);
}
/**
* 无条件翻页查询
*
* @param page 翻页对象
* @see Wrappers#emptyWrapper()
*/
default <E extends IPage<T>> E page(E page) {
return page(page, Wrappers.emptyWrapper());
}
/**
* 查询列表
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
return getBaseMapper().selectMaps(queryWrapper);
}
/**
* 查询所有列表
*
* @see Wrappers#emptyWrapper()
*/
default List<Map<String, Object>> listMaps() {
return listMaps(Wrappers.emptyWrapper());
}
/**
* 查询全部记录
*/
default List<Object> listObjs() {
return listObjs(Function.identity());
}
/**
* 查询全部记录
*
* @param mapper 转换函数
*/
default <V> List<V> listObjs(Function<? super Object, V> mapper) {
return listObjs(Wrappers.emptyWrapper(), mapper);
}
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default List<Object> listObjs(Wrapper<T> queryWrapper) {
return listObjs(queryWrapper, Function.identity());
}
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
* @param mapper 转换函数
*/
default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
return getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
}
/**
* 翻页查询
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
return getBaseMapper().selectMapsPage(page, queryWrapper);
}
/**
* 无条件翻页查询
*
* @param page 翻页对象
* @see Wrappers#emptyWrapper()
*/
default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
return pageMaps(page, Wrappers.emptyWrapper());
}
/**
* 获取对应 entity 的 BaseMapper
*
* @return BaseMapper
*/
BaseMapper<T> getBaseMapper();
/**
* 以下的方法使用介绍:
*
* 一. 名称介绍
* 1. 方法名带有 query 的为对数据的查询操作, 方法名带有 update 的为对数据的修改操作
* 2. 方法名带有 lambda 的为内部方法入参 column 支持函数式的
*
* 二. 支持介绍
* 1. 方法名带有 query 的支持以 {@link ChainQuery} 内部的方法名结尾进行数据查询操作
* 2. 方法名带有 update 的支持以 {@link ChainUpdate} 内部的方法名为结尾进行数据修改操作
*
* 三. 使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
* 1. 根据条件获取一条数据: `query().eq("column", value).one()`
* 2. 根据条件删除一条数据: `update().eq("column", value).remove()`
*
*/
/**
* 链式查询 普通
*
* @return QueryWrapper 的包装类
*/
default QueryChainWrapper<T> query() {
return ChainWrappers.queryChain(getBaseMapper());
}
/**
* 链式查询 lambda 式
* 注意:不支持 Kotlin
*
* @return LambdaQueryWrapper 的包装类
*/
default LambdaQueryChainWrapper<T> lambdaQuery() {
return ChainWrappers.lambdaQueryChain(getBaseMapper());
}
/**
* 链式更改 普通
*
* @return UpdateWrapper 的包装类
*/
default UpdateChainWrapper<T> update() {
return ChainWrappers.updateChain(getBaseMapper());
}
/**
* 链式更改 lambda 式
* 注意:不支持 Kotlin
*
* @return LambdaUpdateWrapper 的包装类
*/
default LambdaUpdateChainWrapper<T> lambdaUpdate() {
return ChainWrappers.lambdaUpdateChain(getBaseMapper());
}
/**
*
* 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
* 此次修改主要是减少了此项业务代码的代码量(存在性验证之后的saveOrUpdate操作)
*
*
* @param entity 实体对象
*/
default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
return update(entity, updateWrapper) || saveOrUpdate(entity);
}
}
初始的搭建项目可看这篇文章:Springboot整合MybatisPlus的基本CRUD
在这个类中有一个变量:int DEFAULT_BATCH_SIZE = 1000;
,该变量为默认批次提交的数量。
由上述源码可看到,还会使用一部分Mapper类中的功能函数,比如save是使用Mapper中的insert函数
目前数据库表内容如下:
count为返回的记录个数
@Test
public void test14(){
// 返回数据的记录数
long result =userService.count();
System.out.println(result);
}
新增功能 save 函数整体如下:
该函数,不管主键ID存在与否,都直接是插入一个新数据
函数 | 大致描述 |
---|---|
boolean save(T entity) |
插入一条记录 |
boolean saveBatch(Collection |
批量插入对象集合,默认是1000条一次 |
saveBatch(Collection |
批量插入集合 |
示例函数如下:()
@Test
public void test1(){
// 插入一条数据
User user = new User();
user.setUsername("user8");
user.setPassword("pass8");
userService.save(user);
System.out.println(user);
// 批量插入数据
User user1 = new User();
user1.setUsername("user9");user1.setPassword("pass9");
User user2 = new User();
user2.setUsername("user10");user2.setPassword("pass10");
List<User> lists = Arrays.asList(user1,user2);
userService.saveBatch(lists);
lists.forEach(System.out::println);
}
对应的数据表如下:
对应上述的封装,也可是用这种方式,但原先的构造可能会失效:User user1 = new User();
//批量插入的时候可以使用bulid来封装
User user3 = User.builder().username("user11").password("pass11").build();
User user4 = User.builder().username("user12").password("pass12").build();
lists = Arrays.asList(user3,user4);
userService.saveBatch(lists);
lists.forEach(System.out::println);
前提需要在entity实体类中加入该注解:@Builder
大致如下:
@Builder
@Data
//@AllArgsConstructor
//@NoArgsConstructor
@TableName("test_user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private int id;
private String username;
private String password;
// 其他字段...
}
新增功能 saveOrUpdate函数整体如下:
对于该功能函数,主要逻辑如下:
函数 | 大致描述 |
---|---|
boolean saveOrUpdate(T entity) |
TableId 注解存在更新记录,否插入一条记录 |
boolean saveOrUpdate(T entity, Wrapper |
根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 |
boolean saveOrUpdateBatch(Collection |
批量修改插入,默认是1000条一次 |
boolean saveOrUpdateBatch(Collection |
批量修改插入 |
对应的代码如下:
@Test
public void test6(){
// 插入的数据不带主键ID值
User user1 = new User();
user1.setUsername("user11");user1.setPassword("pass11");
userService.saveOrUpdate(user1);
System.out.println(user1);
// 插入的数据带主键ID值,且数据库中原先存在该ID值
User user2 = new User();
user2.setId(11);
user2.setUsername("update_user11");user2.setPassword("update_pass11");
userService.saveOrUpdate(user2);
System.out.println(user2);
// 插入的数据带主键ID值,且数据库中原先不存在该ID值
User user3 = new User();
user3.setId(12);
user3.setUsername("user12");user3.setPassword("pass12");
userService.saveOrUpdate(user3);
System.out.println(user3);
}
查看终端中的输出值:
查看数据库值:
删除功能函数整体如下:
函数 | 大致描述 |
---|---|
boolean removeById(Serializable id) |
根据主键id删除 |
boolean removeByMap(Map |
根据Map进行删除 |
boolean remove(Wrapper |
根据entity进行删除 |
boolean removeByIds(Collection idList) |
批量根据主键ID进行删除 |
@Test
public void test7(){
// 删除数据
Boolean result1 = userService.removeById(12);
System.out.println(result1);
// 根据Map进行删除
HashMap<String,Object>map = new HashMap<>();
map.put("username","user13");
map.put("password","pass13");
Boolean result2 = userService.removeByMap(map);
System.out.println(result2);
}
表中会删除ID为12的行
对应如果删除模糊的数据,或者加一些判断,可以这样写:
@Test
public void test9(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("username","update");
userService.remove(wrapper);
}
最终的数据表如下所示:
修改功能函数整体如下:
函数 | 大致描述 |
---|---|
boolean updateById(T entity) |
根据ID进行修改 |
boolean update(Wrapper |
根据 UpdateWrapper 条件,更新记录 需要设置sqlset |
boolean update(T entity, Wrapper |
根据 whereEntity 条件,更新记录 |
boolean updateBatchById(Collection |
根据ID 批量更新 |
updateBatchById(Collection |
根据ID 批量更新 |
对于updateById 的是通过entity类进行传输,且updateBatchById 也就是 updateById 一个扩展版,通过list集合进行叠加,上述代码也有过展示
@Test
public void test10(){
User user = new User();
user.setUsername("user11");user.setPassword("pass11");
userService.updateById(user);
}
修改功能函数整体如下:
注意get功能只能查一条,而list可以查多条
函数 | 大致描述 |
---|---|
T getById(Serializable id) |
根据 ID 查询 |
T getOne(Wrapper |
根据 Wrapper,查询一条记录,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last(“LIMIT 1”) |
T getOne(Wrapper |
根据 Wrapper,查询一条记录。有多个 result 是否抛出异常 |
Map |
根据 Wrapper,查询一条记录 |
|
根据 Wrapper,查询一条记录 |
@Test
public void test11(){
// 根据ID主键查询
User user = userService.getById(1);
System.out.println(user);
// 根据Wrapper查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username","user5");
User user1 = userService.getOne(wrapper);
System.out.println(user1);
}
截图如下:
函数 | 大致描述 |
---|---|
List |
查询(根据ID 批量查询) |
List |
查询(根据 columnMap 条件) |
List |
查询列表 |
List |
查询所有 |
List |
查询列表 |
List |
查询所有列表 |
List |
查询全部记录 |
@Test
public void test12(){
// 查询所有,返回数据是实体类集合
List<User> result =userService.list();
System.out.println(result);
// 根据Id批量查询
List<Integer> list = Arrays.asList(1,2);
result =userService.listByIds(list);
System.out.println(result);
// 根据 columnMap 条件查询
HashMap<String,Object> map = new HashMap<>();
map.put("username","user9");
map.put("password","pass9");
result =userService.listByMap(map);
System.out.println(result);
// 查询所有列表-返回map集合
List<Map<String, Object>> result1 =userService.listMaps();
System.out.println(result1);
// 查询全部记录-返回id集合
List<Object> result2 =userService.listObjs();
System.out.println(result2);
}
截图如下:
函数 | 大致描述 |
---|---|
|
翻页查询 |
|
无条件翻页查询 |
@Test
public void test13(){
// 无条件分页查询-返回实体
// 第一个参数为当前页,第二个参数为页面大小
//第一种写法
Page<User> page1 = new Page<>(2,3);
userService.page(page1,null);
page1.getRecords().forEach(System.out::println);
//总页数
System.out.println(page1.getPages());
//第二种写法
page1 = userService.page(new Page<>(1,2),null);
page1.getRecords().forEach(System.out::println);
// 总页数
System.out.println(page1.getPages());
/*
* -----------------------
* */
//无条件分页查询-返回map
//第一种写法
Page<Map<String, Object>> page2 = new Page<>(2,3);
userService.pageMaps(page2,null);
page2.getRecords().forEach(System.out::println);
//总页数
System.out.println(page2.getPages());
//第二种写法
Page<Map<String, Object>> page3 = userService.pageMaps(new Page<>(1,2),null);
page3.getRecords().forEach(System.out::println);
System.out.println(page3.getPages());
}
使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
* 1. 根据条件获取一条数据: query().eq("column", value).one()
* 2. 根据条件删除一条数据: update().eq("column", value).remove()
函数 | 大致描述 |
---|---|
QueryChainWrapper |
链式查询 普通 |
LambdaQueryChainWrapper |
链式查询 lambda 式 |
UpdateChainWrapper |
链式更改 普通 |
LambdaUpdateChainWrapper |
链式更改 lambda 式 |
对应的查询代码如下:
@Test
public void test14(){
// 查询一条,返回数据是实体类
User result1 = userService.query().eq("username", "user1").one();
System.out.println(result1);
// 查询多条,返回数据是实体类集合
List<User> result2 =userService.query().eq("username", "user1").isNotNull("username").list();
System.out.println(result2);
// 另外的写法,lambdaQuery
List<User> result3 =userService.lambdaQuery().eq(User::getUsername,"user1").list();
System.out.println(result3);
}
截图如下所示:
目前表数据如下:
@Test
public void test15(){
User user1 = new User();
user1.setId(1);user1.setPassword("pass1111");
Boolean result = userService.update().eq("username", "user1").isNotNull("username").update(user1);
System.out.println(result);
User user2 = new User();
user2.setPassword("pass1111");
result = userService.update().eq("username", "user1").isNotNull("username").update(user2);
System.out.println(result);
User user3 = new User();
user3.setId(20);user3.setPassword("pass1111");
result = userService.update().eq("username", "user1").isNotNull("username").update(user3);
System.out.println(result);
}
最终的输出如下:
而且表中的数据如下:
说明有无主键都可返回true