MybatisPlus - 核心功能

在这里插入图片描述

官网
MybatisPlus无侵入和方便快捷.
MybatisPlus不仅仅可以简化单表操作,而且还对Mybatis的功能有很多的增强。可以让我们的开发更加的简单,高效。
通过今天的学习,我们要达成下面的目标:

  • 能利用MybatisPlus实现基本的CRUD
  • 会使用条件构建造器构建查询和更新语句

1.MybatisPlus实现基本的CRUD

快速开始

初始化工程: 配置文件

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
logging:
  level:
    com.itheima: debug
  pattern:
    dateformat: HH:mm:ss
mybatis:
  mapper-locations: classpath*:mapper/*.xml

实体类

@Data
public class User {
    
    

    /**
     * 用户id
     */
    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 注册手机号
     */
    private String phone;

    /**
     * 详细信息
     */
    private String info;

    /**
     * 使用状态(1正常 2冻结)
     */
    private Integer status;

    /**
     * 账户余额
     */
    private Integer balance;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

dao层/mapper层

public interface UserMapper{
    
    

    void saveUser(User user);

    void deleteUser(Long id);

    void updateUser(User user);

    User queryUserById(@Param("id") Long id);

    List<User> queryUserByIds(@Param("ids") List<Long> ids);
}

mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mp.mapper.UserMapper">
    <insert id="saveUser" parameterType="com.itheima.mp.domain.po.User">
        INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
        VALUES
        (#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});
    </insert>
    <update id="updateUser" parameterType="com.itheima.mp.domain.po.User">
        UPDATE `user`
        <set>
            <if test="username != null">
                `username`=#{username}
            </if>
            <if test="password != null">
                `password`=#{password}
            </if>
            <if test="phone != null">
                `phone`=#{phone}
            </if>
            <if test="info != null">
                `info`=#{info}
            </if>
            <if test="status != null">
                `status`=#{status}
            </if>
            <if test="balance != null">
                `balance`=#{balance}
            </if>
        </set>
        WHERE `id`=#{id};
    </update>
    <delete id="deleteUser" parameterType="com.itheima.mp.domain.po.User">
        DELETE FROM user WHERE id = #{id}
    </delete>

    <select id="queryUserById" resultType="com.itheima.mp.domain.po.User">
        SELECT *
        FROM user
        WHERE id = #{id}
    </select>

    <select id="queryUserByIds" resultType="com.itheima.mp.domain.po.User">
        SELECT *
        FROM user
        <if test="ids != null">
            WHERE id IN
            <foreach collection="ids" open="(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
        LIMIT 10
    </select>

</mapper>

现在我们来改造!用mp快速体验一下:

<dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>

在这里插入图片描述

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper extends BaseMapper<User> {
    
    
}

把那些方法全部都删除了,然后把mapper.xml文件也一并删除.我们继承的这个"爸爸"里面这些方法它都有,所以我们直接去测试类测试即可.

扫描二维码关注公众号,回复: 17507854 查看本文章
import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;
import java.util.List;

@SpringBootTest
class UserMapperTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
    
    
        User user = new User();
        user.setId(5L);
        user.setUsername("Lucy");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.insert(user);
    }

    @Test
    void testSelectById() {
    
    
        User user = userMapper.selectById(5L);
        System.out.println("user = " + user);
    }


    @Test
    void testQueryByIds() {
    
    
        List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));
        users.forEach(System.out::println);
    }

    @Test
    void testUpdateById() {
    
    
        User user = new User();
        user.setId(5L);
        user.setBalance(20000);
        userMapper.updateById(user);
    }

    @Test
    void testDeleteUser() {
    
    
        userMapper.deleteById(5L);
    }
}

全部都能测试成功,而且updateById语句如果你没有修改其他的字段那么其他字段不会修改.这样实现单表增删改查全部搞定.

常见注解

MybatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息.
在这里插入图片描述
在这里插入图片描述

@Data
@TableName("user")
public class User {
    
    

    /**
     * 用户id
     */
    @TableId(type = IdType.AUTO)//默认使用雪花算法生成id
    private Long id;

    /**
     * 用户名
     */
//    @TableField("`username`")
    private String username;

    /**
     * 密码
     */
//    @TableField(exist = false)
    private String password;

    /**
     * 注册手机号
     */
    private String phone;

    /**
     * 详细信息
     */
    private String info;

    /**
     * 使用状态(1正常 2冻结)
     */
    private Integer status;

    /**
     * 账户余额
     */
    private Integer balance;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}
常见配置

在这里插入图片描述

但是其实大部分都是默认配置具体可以看官网那些是默认配置

在这里插入图片描述

mybatis-plus:
  type-aliases-package: com.itheima.mp.domain.po
  global-config:
    db-config:
      id-type: auto

2.使用条件构建造器构建查询和更新语句

核心功能:

条件构造器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  @Test
    void testQueryWrapper(){
    
    
        Wrapper<User> wrapper = new QueryWrapper<User>()
                .select("id", "username", "info","balance")
                        .like("username", "o")
                                .ge("balance", 1000);

        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

debug信息:

14:40:41 DEBUG 26264 — [ main] c.i.mp.mapper.UserMapper.selectList : ==> Preparing: SELECT id,username,info,balance FROM user WHERE (username LIKE ? AND balance >= ?)
14:40:41 DEBUG 26264 — [ main] c.i.mp.mapper.UserMapper.selectList : > Parameters: %o%(String), 1000(Integer)
14:40:41 DEBUG 26264 — [ main] c.i.mp.mapper.UserMapper.selectList : <
Total: 1

  @Test
    void testUpdateWrapper(){
    
    
        //1.要更新的数据
        User user = new User();
        user.setBalance(2000);
        //2.更新的条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");
        //3.执行更新
        userMapper.update(user,wrapper);
    }

debug日志信息:

14:45:32 DEBUG 15964 — [ main] com.itheima.mp.mapper.UserMapper.update : ==> Preparing: UPDATE user SET balance=? WHERE (username = ?)
14:45:32 DEBUG 15964 — [ main] com.itheima.mp.mapper.UserMapper.update : > Parameters: 2000(Integer), jack(String)
14:45:32 DEBUG 15964 — [ main] com.itheima.mp.mapper.UserMapper.update : <
Updates: 1

在这里插入图片描述

@Test
    void testUpdateWrapper(){
    
    
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance + 200")
                .in("id", List.of(1L, 2L, 3L, 4L));
        userMapper.update(null,wrapper);
    }

在这里插入图片描述
但是这种写法是硬编码.
推荐Lambda

 @Test
    void testLambdaQueryWrapper(){
    
    
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .select(User::getId, User::getUsername,User::getInfo, User::getBalance)
                        .like(User::getUsername, "o")
                                .ge(User::getBalance, 1000);

        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

在这里插入图片描述

自定义SQL

我们可以利用MybatisPlus的wrapper来构建where条件,然后自己定义SQL语句中剩下部分.
在这里插入图片描述
这样确实方便但是这是在业务层中,这在很多企业中是不允许的.
在这里插入图片描述
在这里插入图片描述

@Test
   void testCustomSql(){
    
    
       List<Long>ids = List.of(1L, 2L, 3L, 4L);
       int amount = 200;
       QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
       userMapper.updateBalanceByIds(wrapper, amount);
   }
public interface UserMapper extends BaseMapper<User> {
    
    
    @Update("UPDATE user set balance = balance + #{amount} ${ew.customSqlSegment}")
    void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper, @Param("amount") int amount);
}
Service接口

在这里插入图片描述
在这里插入图片描述

@SpringBootTest
class IUserServiceTest {
    
    

    @Autowired
    private IUserService userService;

    @Test
    void testSave(){
    
    
        User user = new User();
//        user.setId(5L);
        user.setUsername("yyy");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());

        userService.save(user);

    }

    @Test
    void testQuery(){
    
    
        List<User> users = userService.listByIds(List.of(1L, 2L, 3L));
        users.forEach(System.out::println);
    }

}

我们发现Iservice 和 baseMapper其实有很多方法都是重复的,从功能上是一致的,那我们应该如何选择?这里给出一个案例来进行讲解:
在这里插入图片描述

<!--swagger-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>
<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后配置swagger信息

knife4j:
  enable: true
  openapi:
    title: 用户管理接口文档
    description: "用户管理接口文档"
    email: zhanghuyi@itcast.cn
    concat: 虎哥
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.itheima.mp.controller
@Data
@ApiModel(description = "用户表单实体")
public class UserFormDTO {
    
    

    @ApiModelProperty("id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("密码")
    private String password;

    @ApiModelProperty("注册手机号")
    private String phone;

    @ApiModelProperty("详细信息,JSON风格")
    private String info;

    @ApiModelProperty("账户余额")
    private Integer balance;
}

@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
    
    

    @ApiModelProperty("用户id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("详细信息")
    private String info;

    @ApiModelProperty("使用状态(1正常 2冻结)")
    private Integer status;

    @ApiModelProperty("账户余额")
    private Integer balance;
}

@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
    
    


    private final IUserService userService;// 注入userService 有final才注入

//    public UserController(IUserService userService) {
    
    
//        this.userService = userService;
//    }

    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUser(@RequestBody UserFormDTO userDTO){
    
    
        //1.DTO拷贝1到PO
        User user = BeanUtil.copyProperties(userDTO, User.class);
        //2.新增
        userService.save(user);
    }


    @ApiOperation("删除用户接口")
    @DeleteMapping("/{id}")
    public void DeleteUser(@ApiParam(name = "id", value = "用户id", required = true) @PathVariable("id") Long id){
    
    
        userService.removeById(id);
    }


    @ApiOperation("根据id查询用户接口")
    @GetMapping("/{id}")
    public UserVO queryUser(@ApiParam(name = "id", value = "用户id", required = true) @PathVariable("id") Long id){
    
    
        User user = userService.getById(id);
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        return userVO;
    }

    @ApiOperation("根据id批量查询用户接口")
    @PostMapping
    public List<UserVO> queryUser(@RequestParam("ids") List<Long> ids){
    
    
        List<User> users = userService.listByIds(ids);
        return BeanUtil.copyToList(users, UserVO.class);
    }


}

以后在大多数场景下直接用IService就可以了,因为这几个没有业务逻辑。
第五个接口是有业务的接口,要判断状态,判断余额数量等等。。
在controller中

@ApiOperation("扣减用户余额接口")
    @PutMapping("/{id}/deduction/{money}")
    public void deduction(@ApiParam(name = "id", value = "用户id", required = true) @PathVariable("id") Long id,
                          @ApiParam(name = "money", value = "扣减金额", required = true) @PathVariable("money") Integer money) {
    
    
        userService.deduction(id, money);
    }

service层

public interface IUserService extends IService<User> {
    
    
    void deduction(Long id, Integer money);
}

serviceImpl

@Service
public class UserServiceImlp extends ServiceImpl<UserMapper, User> implements IUserService  {
    
    
    @Override
    public void deduction(Long id, Integer money) {
    
    
        //1. 根据id查询用户
        User user = this.getById(id);
        //2.检验用户状态
        if(user==null || user.getStatus()!=1){
    
    
            throw new RuntimeException("用户状态异常");
        }

        //3.检验余额是否充足
        if(user.getBalance()<money){
    
    
            throw new RuntimeException("余额不足");
        }

        //4.扣除余额
        baseMapper.deductBalance(id,money);
    }
}

mapper:

public interface UserMapper extends BaseMapper<User> {
    
    
    @Update("UPDATE user set balance = balance + #{amount} ${ew.customSqlSegment}")
    void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper, @Param("amount") int amount);

    @Update("UPDATE user set balance = balance - #{money} WHERE id = #{id}")
    void deductBalance(@Param("id") Long id,@Param("money") Integer money);
}

import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
    
    


    private final IUserService userService;// 注入userService 有final才注入

//    public UserController(IUserService userService) {
    
    
//        this.userService = userService;
//    }

    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUser(@RequestBody UserFormDTO userDTO){
    
    
        //1.DTO拷贝1到PO
        User user = BeanUtil.copyProperties(userDTO, User.class);
        //2.新增
        userService.save(user);
    }


    @ApiOperation("删除用户接口")
    @DeleteMapping("/{id}")
    public void DeleteUser(@ApiParam(name = "id", value = "用户id", required = true) @PathVariable("id") Long id){
    
    
        userService.removeById(id);
    }


    @ApiOperation("根据id查询用户接口")
    @GetMapping("/{id}")
    public UserVO queryUser(@ApiParam(name = "id", value = "用户id", required = true) @PathVariable("id") Long id){
    
    
        User user = userService.getById(id);
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        return userVO;
    }

    @ApiOperation("根据id批量查询用户接口")
    @PostMapping
    public List<UserVO> queryUser(@RequestParam("ids") List<Long> ids){
    
    
        List<User> users = userService.listByIds(ids);
        return BeanUtil.copyToList(users, UserVO.class);
    }

    @ApiOperation("扣减用户余额接口")
    @PutMapping("/{id}/deduction/{money}")
    public void deduction(@ApiParam(name = "id", value = "用户id", required = true) @PathVariable("id") Long id,
                          @ApiParam(name = "money", value = "扣减金额", required = true) @PathVariable("money") Integer money) {
    
    
        userService.deduction(id, money);
    }


}

在这里插入图片描述
query:

@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {
    
    
    @ApiModelProperty("用户名关键字")
    private String name;
    @ApiModelProperty("用户状态:1-正常,2-冻结")
    private Integer status;
    @ApiModelProperty("余额最小值")
    private Integer minBalance;
    @ApiModelProperty("余额最大值")
    private Integer maxBalance;
}

controller:

@ApiOperation("根据复杂条件查询用户接口")
    @GetMapping("/list")
    public List<UserVO> queryUserByCondition(UserQuery query){
    
    
        List<User>users = userService.queryUsers(query.getName(),query.getStatus(),query.getMinBalance(),query.getMaxBalance())
        return BeanUtil.copyToList(users, UserVO.class);
    }

IUservice:

public interface IUserService extends IService<User> {
    
    
    void deduction(Long id, Integer money);

    List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
}

serviceimpl

public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
    
    

        return  lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .ge(minBalance != null, User::getBalance, minBalance)
                .le(name != null, User::getBalance, maxBalance)
                .list();
    }

在这里插入图片描述

@Service
public class UserServiceImlp extends ServiceImpl<UserMapper, User> implements IUserService  {
    
    
    @Transactional
    public void deduction(Long id, Integer money) {
    
    
        //1. 根据id查询用户
        User user = this.getById(id);
        //2.检验用户状态
        if(user==null || user.getStatus()!=1){
    
    
            throw new RuntimeException("用户状态异常");
        }

        //3.检验余额是否充足
        if(user.getBalance()<money){
    
    
            throw new RuntimeException("余额不足");
        }

        //4.扣除余额
//        baseMapper.deductBalance(id,money);
        int remainBalance = user.getBalance()-money;//乐观锁 compare and set
        lambdaUpdate()
                .set(User::getBalance,remainBalance)
                .set(remainBalance==0,User::getStatus,2)
                .eq(User::getId,id)
                .eq(User::getBalance,user.getBalance()) //乐观锁 让并发安全性更好
                .update();//不要忘记这一条
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面哪一种一条条执行,每一次提交都是一次网络请求,每次网络请求都要耗费一定的时间,而下面每次插入1000条,采取预编译方案,相当于每1000次才发一次网络请求
在这里插入图片描述
如何把我们的sql语句变成这样?用动态SQL,foreach,或者还是使用mp的批处理指令,但是你会发现这个sql语句不对啊,但其实这个不对只是缺少了一个参数rewriteBatchedStatements=true参数,这个配置是mysql的配置,默认为false所以我们之前以为的批处理实际上都是一条条的插入,所以我们要改这个配置

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root

在url后面加上这个参数即可

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/2301_79602614/article/details/143449952