【SpringBoot+MP】针对复杂业务来手动封装一些涉及到多表操作的删除、分页查询方法

前言

最近也是遇到了一些比较复杂的业务,MP内部提供的方法显然已经不能解决问题,针对场景需要自己手动封装一些方法来用,也是让自己明白了项目不单单都是简单的CRUD,涉及到多表还是比较复杂。

一.扩展MP提供的方法

场景一(删除)

在以前学习OOP中继承的时候,讲到在一组继承关系中,为了提高代码复用性,可扩展父类的方法。
大家都知道,MP的特色就是开发者不用写SQL,而这背后的原理是在一次又一次接口实现、类的继承中体现的(直接调用事先封装好的方法),那如果他提供的方法不能用于特定场景,是不是也可以在接口中扩展一下。
在这里插入图片描述
既然并不能帮助我们解决万难,那么在一些特殊的场景我们不能局限于使用它提供的方法,针对场景要进行适当“改装”。所以,当“时机成熟之时”我们可以扩展父类的方法,扩展功能

在这里插入图片描述
当实体间存在联系,也就是几张表之间相互关联。如果想要删除表中的一则信息我们肯定是要考虑他与其他实体的联系,比如:我删除一个菜类表中的一条数据,这个菜类下可能有菜品,也可能关联套餐,如果直接remove掉选中的菜类,那么我另两个实体中的数据就会丢失!
在这里插入图片描述
所以,我如果想要删除菜类的数据,直接通过MP提供的remove()方法显然是不够严谨全面的。在通过service调用remove()方法之前得加上贴合实际场景的判断条件。
这就要求我们对MP接口里的方法进行扩展:
怎么改进呢?反正层与层之间是继承、实现,那我在接口里重新定义一个remove()方法,不去用他给我提供的不就🆗了

在这里插入图片描述
类似于这样一个场景:
此时,前端已经向我们发来了请求:
在这里插入图片描述
在其实现类里,重写该方法,并加上针对“是否关联菜类,套餐”做一个逻辑判断,若不关联则直接调用父类的方法删除(直接删除无影响),反之则进入if()判断条件,抛出异常并终止删除操作!
怎么才能表示他有关联菜品,套餐呢?
直接根据相同的id查询就可,并返回查询到的行数是否大于0来作为判断条件
在实现类中,通过MP实现起来也就变得十分清晰明了,简简单单,就像这样:

/**
 * 这个方法是我手动封装的!改装的!为了就是解决特殊情况
 * 接收来自前端-接口-实现类的形参中ids参数并进行等值查询
 * @param ids
 */
@Override
public void remove(Long ids) {

    //条件构造器
    LambdaQueryWrapper<Dish> lqw1 = new LambdaQueryWrapper<>();
    //添加查询条件
    lqw1.eq(Dish::getCategoryId, ids);
    //返回查询行数
    int count1 = dishService.count(lqw1);
    //查询当前分类是否关联了菜品,如果关联就抛出一个业务异常
    if (count1 > 0) {
        //已将关联菜品,则抛出一个定义好的业务异常
        throw new CustomException("菜类已关联彩菜品,无法删除");
    }
    LambdaQueryWrapper<Setmeal> lqw2 = new LambdaQueryWrapper<>();
    lqw2.eq(Setmeal::getCategoryId, ids);
    int count2 = setmealService.count(lqw2);
    if (count2 > 0) {
        throw new CustomException("菜类已关联套餐,无法删除");
    }
    //如果不关联  则直接用框架的方法根据id删除它
    super.removeById(ids);
}

上述的方法已经被我写到了实现类中,在Controller层里我们注入该类对应的接口即可使用自己扩展的remove()方法了!
在这里插入图片描述

二.多表操作与事务

场景二(保存)

同样地,在一个保存前端信息的场景中,由于前端的信息涉及到了两张表,我的实体不能一次性封装所有的数据,我需要扩展实体类来封装信息,而这就涉及到了多表的操作,也需要在Service层接口中扩展一个新的方法来处理两张表的信息。

首先定义一个新的DishDto实体类,通过继承获得了Dish的属性,为了能够保存DishFlavor表中的属性我在此类中做出如下扩展:

@Data
public class DishDto extends Dish {
    //用于数据传输 由于Dish中没有flavor属性,所以需要此类来扩展此类
    private List<DishFlavor> flavors=new ArrayList<DishFlavor>(); //接收页面提交的flavor属性
}

经常使用MP的都知道,一般都是一张表对应一个实体类和一个Service,为了少写不必要的表,我直接在在形参里传入DishDto来封装前端的数据,然后在方法里操作DishDto中属于各自表的信息

在这里插入图片描述
那么,在方法里是怎么操作多表的?
在这里插入图片描述
前端的信息已经被封装到了形参中的实体,为了将菜品的基本信息保存到dish表可以直接用Dish的Service对象来调用save()方法,为了保存DishFlavor表中的属性到dish_flavor表则是通过DishFlavor的service来调用saveBatch()保存flavor集合

@Override
@Transactional  //由于涉及到多张表的操作,这里要开启事务
public void saveWithFlavor(DishDto dishDto) {

    //保存菜品基本信息到dish表
    this.save(dishDto);//在这个类里我直接用this

    Long dishId = dishDto.getId();//菜品id

    //菜品口味
    //由于少封装了dishId这里要遍历集合补充一下
    List<DishFlavor> flavors = dishDto.getFlavors();
    flavors=flavors.stream().map((temp)->{  //  stream流来遍历
        temp.setDishId(dishId);
        return temp;
    }).collect(Collectors.toList());

    //保存菜品口味到dish_flavor,保存集合用saveBatch()方法,dishDto.getFlavors()得到口味集合
    dishFlavorService.saveBatch(flavors);
}

有人可能会问,上面保存flavor字段的操作为什么比较复杂,甚至还需要遍历?

由于Java的单继承的机制,自定义的DTO类只继承了Dish类,而这就导致该类丢失了dishId属性。因为DishFlavor表中的属性的保存要和dishId相绑定存入表中,所以在方法里我们需要遍历一遍,给flavor设置dishId。

有人可能会问,你为什么不在DishDto里直接新定义一个属性dishId呢?
在这里插入图片描述
因为我无法给Long型数据指定泛型,而且也没有对应的继承关系,所以只能在方法中通过set方法获得DishFlavor的dishId。

写到这里,已经又一次地在MP的基础上扩展完了方法,而使用到该方法则是在Controller层中直接调用来操作封装前端信息的实体,就像这样:

@PostMapping()
public R<String> save(@RequestBody DishDto dishDto) {
    dishService.saveWithFlavor(dishDto);
    return R.success("菜品信息保存成功!");
}

最后也是成功完成了保存:
在这里插入图片描述

场景三(修改)

在这样一个场景中:更新菜品信息的同时更新口味信息(两者不是一张表不能一次性更改)
在这里插入图片描述
为了实现这一功能,同样需要自己在接口里扩展方法
在这里插入图片描述
在实现类中,针对前端传来的实体类对应的数据,首先应更新菜品信息,也就是修改dish表,可以直接利用MP提供的updateById方法this.updateById(dishDto);来修改
其次,对于DishFlavor表中的属性也就是对dish_flavor表的操作就稍微复杂,得先删除菜品中的口味,后给菜品设置新的口味信息:
(主要通过遍历,来动态绑定id赋予口味信息给菜品)
首先从DishDto里拿到用户选择的口味信息,我们通过.getFlavor()方法拿到口味信息value和name并封装到集合里,由于未与dishId绑定所以我们需要遍历一遍集合,并将DishDto里的dishId赋给集合里的元素
两张表之间是通过dish表的id字段连接dish_flavor表中的dish_id,所以为了连接到,要将dish表的id动态地赋给dish_flavor表中的dish_id

    @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        //根据id选择修改
        this.updateById(dishDto);
        //新建查询,查询口味信息
        LambdaQueryWrapper<DishFlavor> dishDtoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //找出当前菜品对应的口味信息
        dishDtoLambdaQueryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
        //删除指定菜品的口味信息
        dishFlavorService.remove(dishDtoLambdaQueryWrapper);
        //从DishDto里拿到用户选择的口味信息
        List<DishFlavor> flavors = dishDto.getFlavors();
        //遍历一遍flavors赋给他新的id
        flavors = flavors.stream().peek((temp) -> temp.setDishId(dishDto.getId())).collect(Collectors.toList());
        //保存口味信息到dish_flavor表中
        dishFlavorService.saveBatch(flavors);
    }

最后,在Controller层中直接调用扩展的方法,实现功能:
在这里插入图片描述

  • 96
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 117
    评论
### 回答1: 使用 MyBatis-Plus 实现分页功能,可以使用 MyBatis-Plus 的分页插件。 在 Spring Boot 中使用 MyBatis-Plus 分页,需要在配置文件中启用分页插件,例如: ``` mybatis-plus.mapper-locations=classpath:/mapper/*.xml mybatis-plus.type-aliases-package=com.example.domain mybatis-plus.configuration.map-underscore-to-camel-case=true mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl mybatis-plus.configuration.default-fetch-size=100 mybatis-plus.configuration.default-statement-timeout=30 mybatis-plus.configuration.cache-enabled=false mybatis-plus.configuration.jdbc-type-for-null=NULL mybatis-plus.configuration.lazy-loading-enabled=true mybatis-plus.configuration.aggressive-lazy-loading=false mybatis-plus.configuration.multiple-result-sets-enabled=true mybatis-plus.configuration.use-generated-keys=false mybatis-plus.configuration.use-column-label=true mybatis-plus.configuration.auto-mapping-behavior=FULL mybatis-plus.configuration.safe-row-bounds-enabled=false mybatis-plus.configuration.local-cache-scope=SESSION mybatis-plus.configuration.jdbc-fetch-size=100 mybatis-plus.configuration.max-failures-count=10 mybatis-plus.configuration.call-setters-on-nulls=false mybatis-plus.configuration.return-instance-for-empty-row=false mybatis-plus.configuration.map-underscore-to-camel-case=true mybatis-plus.configuration.use-actual-param-name=true mybatis-plus.configuration.log-prefix=mp mybatis-plus.configuration.log-level=debug mybatis-plus.configuration.force-use-generated-keys=false mybatis-plus.configuration.aggressive-lazy-loading=false mybatis-plus.configuration.auto-mapping-behavior=FULL mybatis-plus.configuration.safe-result-handler-enabled=true mybatis-plus.configuration.safe-result-handler-enabled=true mybatis-plus.configuration.auto-mapping-behavior=FULL mybatis-plus.configuration.default-executor-type=SIMPLE mybatis-plus.configuration.default-statement-timeout=25 mybatis-plus.configuration.default-fetch-size=100 mybatis-plus.configuration.safe-row ### 回答2: 使用Spring Boot和MyBatis Plus实现分页功能的步骤如下: 1. 在Spring Boot的项目中引入MyBatis Plus的依赖。可以在pom.xml文件中加入如下代码: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本</version> </dependency> ``` 注意:需将最新版本替换为实际的MyBatis Plus版本号。 2. 创建实体类。根据数据库中的表结构,创建与之对应的实体类,并在类上使用MyBatis Plus提供的注解@Table来指定对应的数据库表名。 3. 创建Mapper接口。创建一个继承自MyBatis Plus提供的BaseMapper接口的自定义Mapper接口,并使用注解@Mapper来标注该接口。 4. 配置分页插件。在application.properties(或application.yaml)配置文件中添加以下配置: ```properties # 分页插件配置 mybatis-plus.pagehelper.helper-dialect=mysql mybatis-plus.pagehelper.reasonable=true mybatis-plus.pagehelper.support-methods-arguments=true ``` 5. 在Service层编写分页查询方法。在Service层的具体方法中,使用MyBatis Plus的Page类来实现分页功能,示例代码如下: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public Page<User> getUserList(int pageNum, int pageSize) { Page<User> page = new Page<>(pageNum, pageSize); QueryWrapper<User> queryWrapper = new QueryWrapper<>(); // 可根据需要添加查询条件 // queryWrapper.eq("name", "xxx"); return userMapper.selectPage(page, queryWrapper); } } ``` 6. 在Controller层调用分页查询方法。在Controller层中,根据请求参数传入需要的页码和每页数量,调用Service层的分页查询方法,并将结果返回给前端。示例代码如下: ```java @Controller public class UserController { @Autowired private UserService userService; @GetMapping("/user/list") public String getUserList(@RequestParam("pageNum") int pageNum, @RequestParam("pageSize") int pageSize, Model model) { Page<User> page = userService.getUserList(pageNum, pageSize); model.addAttribute("userList", page.getRecords()); model.addAttribute("total", page.getTotal()); return "userList"; // 返回到前端页面 } } ``` 以上是使用Spring Boot和MyBatis Plus实现分页功能并返回MySQL数据库中的数据的步骤和示例代码。 ### 回答3: 使用Spring Boot集成MyBatis Plus实现分页功能并返回MySQL数据库数据的方法如下: 1. 首先,在pom.xml文件中添加相关依赖: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> ``` 2. 创建一个实体类,该实体类对应数据库中的表,并使用MyBatis Plus的注解进行配置,如: ```java @TableName("user") public class User { @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField("name") private String name; // 省略getter和setter方法 } ``` 3. 创建Mapper接口,使用MyBatis Plus提供的BaseMapper接口或者继承BaseMapper接口,如: ```java public interface UserMapper extends BaseMapper<User> { } ``` 4. 创建Service层接口和实现类,实现分页查询的功能,如: ```java public interface UserService { IPage<User> getUserList(int pageNum, int pageSize); } @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public IPage<User> getUserList(int pageNum, int pageSize) { Page<User> page = new Page<>(pageNum, pageSize); // 构建分页查询条件 return userMapper.selectPage(page, null); // 进行分页查询 } } ``` 5. 创建Controller层,使用UserService中的方法获取分页数据,并返回给前端,如: ```java @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/users") public IPage<User> getUsers(@RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize) { return userService.getUserList(pageNum, pageSize); } } ``` 6. 最后,配置数据库连接信息,如在application.properties文件中添加以下信息: ``` spring.datasource.url=jdbc:mysql://localhost:3306/database_name spring.datasource.username=your_username spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.jdbc.Driver ``` 以上步骤完成后,启动Spring Boot应用程序,在浏览器访问`localhost:8080/users`即可获取分页数据,并返回到前端。MySQL数据库中的`database_name`为你所使用的数据库名称,`your_username`为你的数据库用户名,`your_password`为你的数据库密码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

懒羊羊.java

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

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

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

打赏作者

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

抵扣说明:

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

余额充值