【Java开发】 Mybatis-Plus 03:IService-CRUD + 性能分析

IService 接口简单来说就是对进 CRUD 的进一步封装,IService 接口与 BaseMapper接口有相同点,也有不同点,相同点在于两者都能够使用条件构造器-Wrapper(之后会介绍~),不同点在于 BaseMapper 接口的CRUD方法与SQL增删改查(insert、delete、update、select)一致,而 IService 接口的 CRUD 方法更易理解(save-增、remove-删、update-改、get-单条查询、list-多条查询、page-分页查询),而且 IService 接口最重要的在于能够使用链式查询/更改(QueryChainWrapper/UpdateChainWrapper),基础配置可见01、02。

目录

1 SQL性能分析打印

1.1 p6spy 依赖引入

1.2 配置

1.3 使用

2 Service CRUD 介绍及准备

2.1 新建 service 软件包及 IBaseService.java 文件

2.2 新建 impl 软件包及 UserServiceImpl.java 文件

2.3 新增 IserviceTset.java 文件

2.4 Service 实践结构

3 Save

3.1 插入一条记录

3.2 builder()方法使用

3.3 批量插入记录

4 SaveOrUpdate

4.1 插入的数据不带id

4.2 插入的数据带id且数据库存在

4.3 插入的数据带id,且数据库不存在

5 Remove

5.1 根据ID删除

 5.2 根据 columnMap 条件删除

6 Update

7 Get

8 List

8.1 查询所有

8.2 根据ID批量查询

8.3 根据 columnMap 条件查询

8.4 查询所有列表-返回map集合

8.5 查询全部记录-返回id集合

9 Page

9.1 无条件分页查询-返回实体

9.2 无条件分页查询-返回map

10 Count

11 Chain 链式查询/更新

11.1 query

11.2 update


源码地址:尹煜 / mybatis_plus_study · GitCode

1 SQL性能分析打印

以下是MybatisPlus官网最新推荐的~

1.1 p6spy 依赖引入

pom.xml文件

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>最新版本</version>
</dependency>

1.2 配置

①配置数据源

路径:src/main/resources/application.properties
#P6Spy配置,联合spy.properties
spring.datasource.url=jdbc:p6spy:mysql://172.0.0.1:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver

②配置spy.properties

路径:src/main/resources/application.properties

注意:# 慢SQL记录标准 2 秒

outagedetectioninterval=2,简单来说,如果sql执行时间超过2秒,就会报错

#性能分析拦截器-SQL分析打印,该插件有性能损耗,不建议生产环境使用。
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

1.3 使用

@Test
void textCapability() {
    //参数是一个wrapper ,条件构造器,这里我们先不用 null
    //查询全部的用户
    List<User> userList = userMapper.selectList(null);
    userList.forEach(System.out::println);
}
接下来的test都会用性能分析哈~

2 Service CRUD 介绍及准备

官方文档:CRUD 接口 | MyBatis-Plus

以下是官网给该接口的官方介绍~

  • 通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD
  • 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆, 泛型 T 为任意实体对象
  • 如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类 对象 Wrapper 为 条件构造器

在 Spring Boot 项目中我们是这样使用该接口的(省去导入包/类代码):

2.1 新建 service 软件包及 IBaseService.java 文件

路径:src/main/java/com/yinyu/service/IBaseService.java

//如有需要用以重写IService里的抽象方法,如不需要重写也可去掉该文件,将IService<User>写在UserServiceImpl文件
public interface IBaseService extends IService<User> {

}

2.2 新建 impl 软件包及 UserServiceImpl.java 文件

路径:src/main/java/com/yinyu/service/impl/UserServiceImpl.java

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IBaseService {
}

2.3 新增 IserviceTset.java 文件

路径:src/test/java/com/yinyu/IserviceTset.java

@SpringBootTest
public class IserviceTset {

    @Autowired
    private UserServiceImpl userService;

    //下边就可以写用例了
}

2.4 Service 实践结构

如此一来,准备工作就已经是完成了~

3 Save

// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);

3.1 插入一条记录

@Test
public void testSave() {
    User user = new User();
    user.setName("Mack");
    user.setAge(18);
    user.setEmail("yinyu@163.com");

    Boolean result = userService.save(user);//帮助用户自动生成id
    System.out.println(result);//插入是否成功
    System.out.println(user);//通过日志发现id会自动回填
}

输出结果:

3.2 builder()方法使用

接下来介绍一个非常好用的 builder() 构造器来高效完成实体类的构建赋值!

①实体类加注解@Builder

路径: src/main/java/com/yinyu/pojo/User.java

②使用build构造器进行赋值

相比之前的构建是不是要清爽许多~

User user1 = User.builder().name("saveBatch1").age(15).email("yinyu@163.com").build();

3.3 批量插入记录

@Test
public void testSaveBatch() {
    User user1 = User.builder().name("saveBatch1").age(15).email("yinyu@163.com").build();
    User user2 = User.builder().name("saveBatch2").age(16).email("yinyu@163.com").build();
    List<User> list = Arrays.asList(user1,user2);

    Boolean result = userService.saveBatch(list);//帮助用户自动生成id
    System.out.println(result);//插入是否成功
    System.out.println(list);//通过日志发现id会自动回填
}

可以看到实质上它是进行了两次插入sql~

4 SaveOrUpdate

// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

这个方法的逻辑是:若存在主键,先执行查找,查询失败返回0(查询失败后执行插入),在执行更新,更新失败返回0,若不存在主键,直接执行插入。

主要存在以下三种情况:

4.1 插入的数据不带id

@Test
public void testSaveOrUpdate1() {
    User user1 = User.builder().name("testSaveOrUpdate1").age(15).email("yinyu@163.com").build();

    Boolean result = userService.saveOrUpdate(user1);//帮助用户自动生成id
    System.out.println(result);//SaveOrUpdate是否成功
}

可以看到执行插入 SQL,这是因为 SaveOrUpdate 是根据 id(主键)来进行判断的,如果能在数据表中匹配到对应的id,那么他才会执行更新操作。

4.2 插入的数据带id且数据库存在

@Test
public void testSaveOrUpdate2() {
    User user1 = User.builder().id(1L).name("testSaveOrUpdate2").age(18).email("yinyu@163.com").build();

    Boolean result = userService.saveOrUpdate(user1);
    System.out.println(result);//SaveOrUpdate是否成功
}

当 id(主键)设为1时,修改成功!可以看到实际上进行了两步操作,查询+更新

4.3 插入的数据带id,且数据库不存在

@Test
public void testSaveOrUpdate3() {
    User user1 = User.builder().id(10L).name("testSaveOrUpdate3").age(18).email("yinyu@163.com").build();

    Boolean result = userService.saveOrUpdate(user1);
    System.out.println(result);//SaveOrUpdate是否成功
}

插入成功,不过也是经历了两步,查询失败+插入~

5 Remove

// 根据 entity 条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);

remove()的入参是Wrapper条件构造器,会在下一篇文章04进行介绍~

5.1 根据ID删除

 顾名思义,当然Remove方法也支持 idList 进行删除。

@Test
public void testRemove1() {
    //删除id=1586599555079684107L的记录
    Boolean result = userService.removeById(1586599555079684107L);
    System.out.println(result);//SaveOrUpdate是否成功
}

因为之前设置了逻辑删除(在02),因此该记录实质上没有删除,而是进行逻辑删除。

 5.2 根据 columnMap 条件删除

@Test
public void testRemove2() {
    //删除 name = 尹煜 , age = 3 的记录
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","尹煜");
    map.put("age",3);

    Boolean result = userService.removeByMap(map);
    System.out.println(result);//SaveOrUpdate是否成功
}

算是多条件删除吧,注意还是逻辑删除哦~

6 Update

// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);

和remove一样,update()的入参也是Wrapper条件构造器,会在下一篇文章04进行介绍~

需要注意的是,updateById 的入参是实体类而不是 id ,因为更新信息只有一个 id 肯定是不够的嘛,而且 id 是必须加进去的!

updateBatchById 也就是 updateById 升级版,入参是实体类的集合,这边就不多演示了。

@Test
public void testUpdate() {
    User user1 = User.builder().id(1L).name("testUpdate").age(18).email("yinyu@163.com").build();

    Boolean result = userService.updateById(user1);
    System.out.println(result);//SaveOrUpdate是否成功
}

更新执行成功!

7 Get

// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);

get 是查询一条数据,不同于 mapper 的 select,iservice 将其分为了 get-查询1条 和 list-查询多条!

@Test
public void testGet() {
    User result = userService.getById(1L); //返回数据是User实体类
    System.out.println(result);//SaveOrUpdate是否成功
}

8 List

// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);

List 方法的功能更为丰富,我贴上了最常用的几个来和大家分享~

8.1 查询所有

@Test
public void testList1() {
    List<User> result =userService.list(); //返回数据是实体类集合
    System.out.println(result);
}

8.2 根据ID批量查询

查询 id=1 和 id=2 的记录~

@Test
public void testList2() {
    List<Long> ids = Arrays.asList(1L,2L);
    List<User> result =userService.listByIds(ids); //返回数据是实体类集合
    System.out.println(result);
}

8.3 根据 columnMap 条件查询

@Test
public void testList3() {
    HashMap<String,Object> map = new HashMap<>();
    map.put("name","尹煜");
    map.put("age",3);

    List<User> result =userService.listByMap(map); //返回数据是实体类集合
    System.out.println(result);
}

8.4 查询所有列表-返回map集合

@Test
public void testList4() {
    List<Map<String, Object>> result =userService.listMaps(); //返回数据是map集合
    System.out.println(result);
}

8.5 查询全部记录-返回id集合

@Test
public void testList5() {
    List<Object> result =userService.listObjs(); //返回数据是Object集合
    System.out.println(result);
}

可以看到,返回的是主键 id 的集合~

9 Page

前提:已配置分页插件(链接:Mybatis-Plus 02-5.2)~

// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

9.1 无条件分页查询-返回实体

@Test
public void testPage1(){
    //参数一current:当前页   参数二size:页面大小
    //写法1:
    Page<User> page1 = new Page<>(2,3);
    userService.page(page1,null);
    page1.getRecords().forEach(System.out::println);
    System.out.println("总页数==>"+page1.getPages());

    //写法2:
    Page<User> page2 = userService.page(new Page<>(1,2),null);
    page2.getRecords().forEach(System.out::println);
    System.out.println("总页数==>"+page2.getPages());
}

可以看到实际进行了两步操作,首先是查询全部记录,然后执行LIMIT语句~

9.2 无条件分页查询-返回map

@Test
public void testPage2(){
    //参数一current:当前页   参数二size:页面大小
    //写法1:
    Page<Map<String, Object>> page1 = new Page<>(2,3);
    userService.pageMaps(page1,null);
    page1.getRecords().forEach(System.out::println);
    System.out.println("总页数==>"+page1.getPages());

    //写法2:
    Page<Map<String, Object>> page2 = userService.pageMaps(new Page<>(1,2),null);
    page2.getRecords().forEach(System.out::println);
    System.out.println("总页数==>"+page2.getPages());
}

可以看到返回的是map的集合~ 

10 Count

// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);
@Test
public void testCount() {
    long result =userService.count(); //返回数据是记录数
    System.out.println(result);
}

11 Chain 链式查询/更新

这算是 service 和 mapper 比较重要的不同点了,这也是进一步解放生产力的方法~

11.1 query

// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery();

①queryone

以.one结尾,返回的是一条数据,若实际返回多条会报错~

@Test
public void testqueryone() {
    User result =userService.query().eq("name", "尹煜").one();; //返回数据是实体类
    System.out.println(result);
}

查询成功~

②querylist

@Test
public void testquerylist() {
    List<User> result =userService.query().eq("age", 18).isNotNull("name").list();; //返回数据是实体类集合
    System.out.println(result);
}

查询成功~

③​lambdaQuery

lambda也就是匿名内部类,入参的话是函数式接口,参考如下👇

@Test
public void testlambdaQuery() {
    List<User> result =userService.lambdaQuery().eq(User::getAge,18).list();; //返回数据是实体类集合
    System.out.println(result);
}

11.2 update

// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();

update 和 query 的操作是类似的,一个是更新,一个是查询,最后以 update(实体类)结尾,更新的逻辑和本文4-SaveOrUpdate不一样👇

实体类不论是否存在主键id,那么满足链式条件的所有数据都会更新成实体类的形式👇

①不存在主键

@Test
public void testupdate() {
    User user1 = User.builder().name("saveBatch1").age(15).email("yinyu@163.com").build();
    Boolean result =userService.update().eq("age", 18).isNotNull("name").update(user1);
    System.out.println(result);
}

①存在主键

@Test
public void testupdate2() {
    User user1 = User.builder().id(1L).name("testupdate2").age(18).email("yinyu@163.com").build();
    Boolean result =userService.update().eq("age", 15).isNotNull("name").update(user1);
    System.out.println(result);
}

可以看到,虽然实体类存在主键,但是其他记录(id不等于1)还是改变了,可能user1是作为一个模板来进行更新,是否有id并不重要~


总结

大家如果有疑问都可以评论提出,有不足之处请大家批评指正,希望能多结识这方面的朋友,共同学习、共同进步。

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尹煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值