目录
- 前言
- 1. 基本概念
- 2. CRUD
- 2.1 插入
- 2.1.1 save
- 2.1.2 saveOrUpdate
- 2.2 删除
- 2.3 修改
- 2.4 查询
- 2.4.1 get
- 2.4.2 list
- 2.4.3 page
- 2.5 chain链式函数
前言
大部分CRUD都来源这个类,对此有意义剖析,方便之后的功能开发
1. 基本概念
在 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,查询一条记录 <br/>* <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>** @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 式* <p>注意:不支持 Kotlin </p>** @return LambdaQueryWrapper 的包装类*/default LambdaQueryChainWrapper<T> lambdaQuery() {return ChainWrappers.lambdaQueryChain(getBaseMapper());}/*** 链式更改 普通** @return UpdateWrapper 的包装类*/default UpdateChainWrapper<T> update() {return ChainWrappers.updateChain(getBaseMapper());}/*** 链式更改 lambda 式* <p>注意:不支持 Kotlin </p>** @return LambdaUpdateWrapper 的包装类*/default LambdaUpdateChainWrapper<T> lambdaUpdate() {return ChainWrappers.lambdaUpdateChain(getBaseMapper());}/*** <p>* 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法* 此次修改主要是减少了此项业务代码的代码量(存在性验证之后的saveOrUpdate操作)* </p>** @param entity 实体对象*/default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {return update(entity, updateWrapper) || saveOrUpdate(entity);}
}
2. CRUD
初始的搭建项目可看这篇文章:Springboot整合MybatisPlus的基本CRUD
在这个类中有一个变量:int DEFAULT_BATCH_SIZE = 1000;
,该变量为默认批次提交的数量。
由上述源码可看到,还会使用一部分Mapper类中的功能函数,比如save是使用Mapper中的insert函数
目前数据库表内容如下:
count为返回的记录个数
@Testpublic void test14(){// 返回数据的记录数long result =userService.count(); System.out.println(result);}
2.1 插入
2.1.1 save
新增功能 save 函数整体如下:
该函数,不管主键ID存在与否,都直接是插入一个新数据
函数 | 大致描述 |
---|---|
boolean save(T entity) | 插入一条记录 |
boolean saveBatch(Collection<T> entityList) | 批量插入对象集合,默认是1000条一次 |
saveBatch(Collection<T> entityList, int batchSize) | 批量插入集合 |
示例函数如下:()
@Testpublic 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;// 其他字段...
}
2.1.2 saveOrUpdate
新增功能 saveOrUpdate函数整体如下:
对于该功能函数,主要逻辑如下:
- 若存在主键ID值,则进行更新
- 若不存在主键ID值,则进行插入
函数 | 大致描述 |
---|---|
boolean saveOrUpdate(T entity) | TableId 注解存在更新记录,否插入一条记录 |
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) | 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 |
boolean saveOrUpdateBatch(Collection<T> entityList) | 批量修改插入,默认是1000条一次 |
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) | 批量修改插入 |
对应的代码如下:
@Testpublic 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);}
查看终端中的输出值:
查看数据库值:
2.2 删除
删除功能函数整体如下:
函数 | 大致描述 |
---|---|
boolean removeById(Serializable id) | 根据主键id删除 |
boolean removeByMap(Map<String, Object> columnMap) | 根据Map进行删除 |
boolean remove(Wrapper<T> queryWrapper) | 根据entity进行删除 |
boolean removeByIds(Collection<? extends Serializable> idList) | 批量根据主键ID进行删除 |
@Testpublic 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的行
对应如果删除模糊的数据,或者加一些判断,可以这样写:
@Testpublic void test9(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like("username","update");userService.remove(wrapper);}
最终的数据表如下所示:
2.3 修改
修改功能函数整体如下:
函数 | 大致描述 |
---|---|
boolean updateById(T entity) | 根据ID进行修改 |
boolean update(Wrapper<T> updateWrapper) | 根据 UpdateWrapper 条件,更新记录 需要设置sqlset |
boolean update(T entity, Wrapper<T> updateWrapper) | 根据 whereEntity 条件,更新记录 |
boolean updateBatchById(Collection<T> entityList) | 根据ID 批量更新 |
updateBatchById(Collection<T> entityList, int batchSize) | 根据ID 批量更新 |
对于updateById 的是通过entity类进行传输,且updateBatchById 也就是 updateById 一个扩展版,通过list集合进行叠加,上述代码也有过展示
@Testpublic void test10(){User user = new User();user.setUsername("user11");user.setPassword("pass11");userService.updateById(user);}
2.4 查询
修改功能函数整体如下:
注意get功能只能查一条,而list可以查多条
2.4.1 get
函数 | 大致描述 |
---|---|
T getById(Serializable id) | 根据 ID 查询 |
T getOne(Wrapper<T> queryWrapper) | 根据 Wrapper,查询一条记录,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last(“LIMIT 1”) |
T getOne(Wrapper<T> queryWrapper, boolean throwEx) | 根据 Wrapper,查询一条记录。有多个 result 是否抛出异常 |
Map<String, Object> getMap(Wrapper<T> queryWrapper) | 根据 Wrapper,查询一条记录 |
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) | 根据 Wrapper,查询一条记录 |
@Testpublic 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);}
截图如下:
2.4.2 list
函数 | 大致描述 |
---|---|
List<T> listByIds(Collection<? extends Serializable> idList) | 查询(根据ID 批量查询) |
List<T> listByMap(Map<String, Object> columnMap) | 查询(根据 columnMap 条件) |
List<T> list(Wrapper<T> queryWrapper) | 查询列表 |
List<T> list() | 查询所有 |
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) | 查询列表 |
List<Map<String, Object>> listMaps() | 查询所有列表 |
List<Object> listObjs() | 查询全部记录 |
@Testpublic 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);}
截图如下:
2.4.3 page
函数 | 大致描述 |
---|---|
<E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) | 翻页查询 |
<E extends IPage<T>> E page(E page) | 无条件翻页查询 |
@Testpublic 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());}
2.5 chain链式函数
使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
* 1. 根据条件获取一条数据: query().eq("column", value).one()
* 2. 根据条件删除一条数据: update().eq("column", value).remove()
函数 | 大致描述 |
---|---|
QueryChainWrapper<T> query() | 链式查询 普通 |
LambdaQueryChainWrapper<T> lambdaQuery() | 链式查询 lambda 式 |
UpdateChainWrapper<T> update() | 链式更改 普通 |
LambdaUpdateChainWrapper<T> lambdaUpdate() | 链式更改 lambda 式 |
对应的查询代码如下:
@Testpublic 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);// 另外的写法,lambdaQueryList<User> result3 =userService.lambdaQuery().eq(User::getUsername,"user1").list();System.out.println(result3);}
截图如下所示:
目前表数据如下:
@Testpublic 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