多表联查
上文讲过了自定义sql ,和wrapper的使用,但是我们可以发现 我们查询的都是数据库中的一张表,那么怎么进行多表联查呢,当然也是用自定义sql来进行实现
比如说 查询 id 为 1 2 4 的用户 并且 地址在北京 的 用户名称 普通的sql查询如下
select u.username from tb_user u,address a where u.id=a.user_id and a.city='北京' and u.id i n(1,2,4);
用join on 实现多表的sql为
select u.username from tb_user u join address a on u.id=a.user_id where a.city='北京' and u.id in(1,2,4);
那么 我们如何用mp 来进行改造 自定义sql 来进行多表查询呢
先用 wrapper 来进行后面 where 条件的拼接 再用自定义sql 拼接
废话不多说 直接上代码
然后再usermapper中定义方法
@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);
service接口
mp不仅对基本的 mapper层进行了接口的封装,还对service层进行了接口的封装,使得 基础的crud也可以直接在controller层直接调用service层直接查询,更加提升了代码的简洁性
而使用Iservice中的方法也很简单
通常我们有两个 一个是接口service,一个是实现接口的实现类 我们只需要让接口继承 iservice,然后让自己的实现类 实现自己的接口,并且继承serviceimpl 废话不多说 我们直接上代码
其中User是对应的数据库实体类 ,同样Usermapper 也是
但是我们这样说是显得太过宽泛 我们直接上例子 对service接口进行测试
接口测试
我们 假如说 现在有一个需求 根据用户的id 来进行扣减用户的余额 ,而在用户表中有一个状态 只有状态正常的
并且余额充足的才能进行余额的扣减
现在 我们在 Usercontroller 中定义 一个方法
//根据id扣除余额
@PutMapping("/{id}/deduction/{money}")
void deductMoney(@PathVariable Long id,
@PathVariable Integer money){
myService.deductMoney(id,money);
}
然后是 userservice接口
package com.itheima.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
public interface IUserService extends IService<User> {
void deductBalance(Long id, Integer money);
}
最后是实现类
package com.itheima.mp.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itheima.mp.domain.po.User; import com.itheima.mp.mapper.UserMapper; import com.itheima.mp.service.IUserService; import org.springframework.stereotype.Service; @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Override public void deductBalance(Long id, Integer money) { // 1.查询用户 User user = getById(id); // 2.判断用户状态 if (user == null || user.getStatus() == 2) { throw new RuntimeException("用户状态异常"); } // 3.判断用户余额 if (user.getBalance() < money) { throw new RuntimeException("用户余额不足"); } // 4.扣减余额 baseMapper.deductMoneyById(id, money); } }
mapper中的实现接口为
@Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
void deductMoneyById(@Param("id") Long id, @Param("money") Integer money);
但是 但是这样是否有点不太优雅 那么 我们怎么把他变得优雅? 来 让我们改造一下
优雅的接口测试
首先 我们可以看到 在 我们对比用户状态的时候 用户状态异常的数字是写死的,这样写有没有问题,没有问题,但是假如我们以后用了多个用户状态的的数字,以后想改 是不是特别 的麻烦 ,所以我们干脆定义一个枚举类型,来进行 用户状态的对比
我们原来user实体类中的类型 是
是interger类型的对比 ,下面 我们 定义一个如下的枚举
并在mp的yaml文件中配置枚举处理器
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
配置好 之后再把user实体类中的 状态的类型 改成 枚举类型
好,那么现在我们来解释一下 枚举类型中的注解
使用enumvalue 注解,代表着 枚举类中的哪个值 作为 数据库status字段的值
比如说 我们 数据库中定义的字段status为 int类型的,然后我们对应的数据库user表的实体类user 的类型是 枚举类型的这样就会导致我们在查询插入的时候出现类型转换的错误,我们加上这个注解,开启配置枚举处理器,就可以 实现二者值之间的自动转换
下面 我们来 写一个例子
@PostMapping
void userSelect(@Param("id") Long id){
//通过枚举类型 加用户id进行查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().eq(User::getId, id).eq(User::getStatus, UserStatus.NORMAL);
User user1 = myService.getBaseMapper().selectOne(wrapper);
System.out.println(user1);
//使用枚举类型插入用户
User user = new User();
//我们着重看这一行 setstatus
user.setStatus(UserStatus.NORMAL);
user.setUsername("wang");
user.setPassword("123456");
UserInfo userInfo = new UserInfo();
userInfo.setAge(1);
userInfo.setGender("22");
userInfo.setIntro("6666666");
user.setInfo(userInfo);
myService.save(user);
}
下面我们用apifox进行测试
可以看到 用了enumvalue注解 已经成功的把枚举类型转换成数据库类型了,是不是灰常的优雅
优雅的json处理器
在上文中我们看到 怎么还有个userinfo 那玩意是什么鬼 ,下面我们来解释一下
在数据库中 我们的info字段定义的是一个json类型
但是 在我么们user实体类中确实 一个string类型的 字段 ,在我们插入的时候 会非常的麻烦 我们需要把字符串类型写成类似于json类型进行插入,这样太麻烦
我们 直接可以定义一个json类型的实体类 ,然后把user实体类的类型换成该json类型的实体类 ,然后再user实体类字段上加上json类型处理器的注解 就可以完成转换
@Data
@AllArgsConstructor(staticName = "set")
@NoArgsConstructor
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
这样我们再插入的时候 就可以完成 json类型之间的转换了