MyBatisPlus全解

BMP入门

Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。

Mybatis-Plus已经封装好了大量增删改查的方法,程序员只需要继承BaseMapper就可以使用这些方法了,无需自己再开发。

官网地址:https://baomidou.com/

案例实现

向数据库保存一个User对象

创建数据表

create table tb_user (
         id bigint(20) primary key auto_increment,
         username varchar(30) unique not null,
         name varchar(30) not null,
         password varchar(32) not null,
         age int(3) not null ,
         tel varchar(32) not null,
         create_time datetime,
         update_time datetime
);
insert into tb_user values(1,'heima1','tom','123456',12,'12345678911',now(),now());
insert into tb_user values(2,'heima2','jack','123456',8,'12345678912',now(),now());
insert into tb_user values(3,'heima3','jerry','123456',15,'12345678910',now(),now());
insert into tb_user values(4,'heima4','tom','123456',9,'12345678910',now(),now());
insert into tb_user values(5,'heima5','snake','123456',28,'12345678910',now(),now());
insert into tb_user values(6,'heima6','张益达','123456',22,'12345678910',now(),now());
insert into tb_user values(7,'heima7','张大炮','123456',16,'12345678910',now(),now());

创建工程

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
    </parent>

    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--mybatis plus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--lombok简化对象书写-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--hutool工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>
        <!--整合测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--web环境依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

创建实体类

package com.itheima.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String username;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

创建接口

自定义的Mapper接口需要实现BaseMapper<实体类>,然后就可以继承到BaseMapper中定义的方法了

package com.itheima.mapper;

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

@Mapper
public interface UserMapper extends BaseMapper<User> {
    
    //声明方法+注解实现sql
    
}

添加配置文件

在resources中添加配置文件application.yml,然后在里面加入下面配置

spring:
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/mp
   username: root
   password: root
   # druid 阿里    HkariCp(springBoot)
   type: com.alibaba.druid.pool.DruidDataSource

mybatis-plus:
 configuration:
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志打印
   map-underscore-to-camel-case: true # 驼峰映射

创建启动类

package com.itheima;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MpApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpApplication.class,args);
    }
}

测试

package com.itheima.test;

import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testInsert() {
        //1. 封装user对象
        User user = User.builder()
                .username("baima1")
                .name("tom")
                .password("123456")
                .age(18)
                .tel("13700137001")
                .build();
        //2. 调用mapper方法
        userMapper.insert(user);
    }
}

案例细节

@TableName

标注在实体类上,用于声明实体类对应的表,如果表名和类名一致可以省略

//标注在实体类上,用于声明实体类对应的表,如果表名和类名一致可以省略
@TableName("tb_user")
public class User {
    ....
}

如果大部分表都是以固定前缀开头,可以全局配置表前缀,但优先级比注解配置低

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_ #表前缀

@TableId

标注在主键字段上,用于声明主键生成策略

    //主键生成策略
    //AUTO : 数据库的自增
    //INPUT: 让用户自己输入主键
    //ASSIGN_ID: 雪花算法生成的一个纯数字
    //ASSIGN_UUID: UUID生成一个不重复字符串
    //ASSIGN_NONE: INPUT+ASSIGN_ID
    @TableId(type = IdType.AUTO)
    private Long id;
描述
AUTO数据库主键自增
INPUT手动设置主键值
ASSIGN_ID由雪花算法生成的纯数字
ASSIGN_UUIDUUID生成的字符串
NONE默认值相当于INPUT+ASSIGN_ID

如果大部分表主键都是自增,可以进行全局设置,但优先级比注解配置低

mybatis-plus:
  global-config:
    db-config:
      id-type: auto #主键策略
      table-prefix: tbl_ #表前缀

Mapper接口

BaseMapper:通用 CRUD 接口,内部声明了大量的单表操作方法,泛型 T 为任意实体对象

@Mapper
public interface ProductMapper extends BaseMapper<Product> {

}

基本使用

MybatisPlus提供了单表的增删改查方法,常用的如下

// 插入一条记录
int insert(T entity);

// 主键查询
T selectById(Serializable id);
// 主键批量查询
List<T> selectBatchIds(Collection idList); 

// 根据ID修改不为空的字段
int updateById(T entity);

// 根据ID删除
int deleteById(Serializable id);

// 根据ID集合批量删除
int deleteBatchIds(Collection idList);
package com.itheima.test;

import cn.hutool.core.collection.ListUtil;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    //插入一条记录
    //int insert(T entity);
    @Test
    public void testInsert() {
        //1. 封装user对象
        User user = User.builder()
                .username("baima1")
                .name("tom")
                .password("123456")
                .age(18)
                .tel("13700137001")
                .build();
        //2. 调用mapper方法
        userMapper.insert(user);
    }


    //主键查询
    //T selectById(Serializable id);
    @Test
    public void testSelectById() {
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }

    //主键批量查询
    //List<T> selectBatchIds(Collection idList);
    @Test
    public void testSelectBatchIds() {
        //1. 构建id集合
        List<Long> idList = ListUtil.of(1L, 2L, 3L);
        //2. 执行查询
        List<User> userList = userMapper.selectBatchIds(idList);
        System.out.println(userList);
    }

    //根据ID修改不为空的字段
    //int updateById(T entity);
    @Test
    public void testUpdateById() {
        //1. 封装user对象
        User user = User.builder()
                .username("baima2")
                .name("tom2")
                .tel("13700137002")
                .id(2L)//不要忘记设置id
                .build();
        userMapper.updateById(user);
    }

    //根据ID删除
    //int deleteById(Serializable id);
    @Test
    public void testDeleteById() {
        userMapper.deleteById(7L);
    }

    //根据ID集合批量删除
    //int deleteBatchIds(Collection idList);
    @Test
    public void testDeleteBatchIds() {
        List<Long> idList = ListUtil.of(1L, 2L, 3L);
        userMapper.deleteBatchIds(idList);
    }
}

条件查询

MybatisPlus提供了Wrapper对象来封装各种条件,比如条件、分页、排序、分组、过滤等等

// 条件查询,当查询结果最多为一条记录时使用  手机号 身份证号  用户名  唯一约束
T selectOne(Wrapper<T> queryWrapper);

// 条件查询,当查询结果可能为多条记录时使用
List<T> selectList(Wrapper<T> queryWrapper);

书写格式

MybatisPlus支持使用多种格式组装条件,我们推荐使用Lambda格式

package com.itheima.test;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;


@SpringBootTest
public class UserMapperTest2 {

    @Autowired
    private UserMapper userMapper;

    //根据name=白马1和age>18查询(支持动态sql)
    @Test
    public void testSelectList1() {
        //0. 模仿前端传入参数
        String name = null;
        Integer age = 18;

        //1. 构建查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(name)) {
            wrapper.eq("name", name); //where name = '白马'
        }
        if (age != null) {
            wrapper.gt("age", age);//and age > 18
        }

        //2. 查询
        List<User> userList = userMapper.selectList(wrapper);

        userList.forEach(System.out::println);
    }

    //Lambda 根据name=白马1和age>18查询(支持动态sql)
    @Test
    public void testSelectList2() {
        //0. 模仿前端传入参数
        String name = "白马";
        Integer age = 18;

        //1. 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //参数1位置是一个boolean值,只有这个值为true,当前条件才会生效
        wrapper.eq(StrUtil.isNotEmpty(name), User::getName, name);//where name = '白马'
        wrapper.gt(age != null, User::getAge, age);//and age > 18

        //2. 查询
        List<User> userList = userMapper.selectList(wrapper);

        userList.forEach(System.out::println);
    }

    //Lambda链式 根据name=白马1和age>18查询(支持动态sql)
    @Test
    public void testSelectList3() {
        //0. 模仿前端传入参数
        String name = "白马";
        Integer age = 18;

        //1. 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //参数1位置是一个boolean值,只有这个值为true,当前条件才会生效
        wrapper.eq(StrUtil.isNotEmpty(name), User::getName, name)//where name = '白马'
                .gt(age != null, User::getAge, age);//and age > 18

        //2. 查询
        List<User> userList = userMapper.selectList(wrapper);

        userList.forEach(System.out::println);
    }
}

查询条件

查询方法说明例子
eq、ne、gt、ge、lt、le、isNull、isNotNull比较运算eq(“name”, “老王”)--->name = ‘老王’
like、notLike、likeLeft、likeRight模糊查询likeRight(“name”, “王”)--->name like ‘王%’
in、notIn、between、notBetween范围运算in(“age”,{1,2,3})--->age in (1,2,3)
or、and拼接eq(“id”,1).or().eq(“name”,“老王”)--->id = 1 or name = ‘老王’
    //select * from tb_user where
    //id >= 1
    //and username = 'baima'
    //and name like '%马'
    //and age between 10 and 30
    //or tel in ('1370013001','1370013002')
    @Test
    public void testSelectList4() {
        //0. 模仿前端传入参数
        String name = "白马";
        Integer age = 18;

        //1. 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(User::getId,1)
                .eq(User::getUsername,"baima")
                .likeLeft(User::getName,'马')
                .between(User::getAge,10,30)
                .or()
                .in(User::getTel,List.of("1370013001","1370013002"));

        //2. 查询
        List<User> userList = userMapper.selectList(wrapper);

        userList.forEach(System.out::println);
    }

其他条件

除了设置查询条件外,MP还支持:投影、排序、分组、过滤等功能

查询方法说明例子
select投影select(“name”,“password”)--->select name,password from 表
orderByAsc、orderByDesc排序orderByDesc(“id”, “name”)--->order by id DESC,name DESC
groupBy分组groupBy(“id”, “name”)--->group by id,name
having过滤having(“sum(age) > 10”)--->having sum(age) > 10
package com.itheima.test;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import com.itheima.vo.Re;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
import java.util.Map;

@SpringBootTest
public class UserMapperTest2 {

    @Autowired
    private UserMapper userMapper;

    //投影和排序
    //select name,age from tb_user where id > 1 order by age desc
    @Test
    public void testSelectList1() {
        //1. 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getId,1);//id > 1
        wrapper.select(User::getName,User::getAge);//select name,age
        wrapper.orderByDesc(User::getAge);//order by age desc
        wrapper.orderByAsc(User::getId);//order by id asc

        //2. 查询
        List<User> userList = userMapper.selectList(wrapper);

        userList.forEach(System.out::println);
    }


    //分组和过滤
    //select age,count(*) from tb_user group by age having count(*) >= 2
    @Test
    public void testSelectList2() {
        //1. 构建查询条件(LambdaQueryWrapper不支持分组和过滤)
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("age","count(*)");//select age,count(*)
        wrapper.groupBy("age");//group by age
        wrapper.having("count(*) >= 2");//having count(*) >= 2

        //2. 查询
        List<Map<String, Object>> list = userMapper.selectMaps(wrapper);

        list.forEach(System.out::println);
    }

    //分组和过滤
    //select age,count(*) from tb_user group by age having count(*) >= 2
    @Test
    public void testSelectList3() {
        List<Map<String, Object>> list = userMapper.count1();
        list.forEach(System.out::println);
    }

    @Test
    public void testSelectList4() {
        List<Re> list = userMapper.count2();
        list.forEach(System.out::println);
    }
}

UserMapper

package com.itheima.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import com.itheima.vo.Re;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;
import java.util.Map;

//自定义的Mapper 要求继承BaseMapper<实体类类型>
@Mapper
public interface UserMapper extends BaseMapper<User> {

    //自定义
    @Select("select age,count(*) from tb_user group by age having count(*) >= 2")
    List<Map<String, Object>> count1();

    @Select("select age,count(*) as num from tb_user group by age having num >= 2")
    List<Re> count2();
}

Re

package com.itheima.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Re {
    private String age;
    private Integer num;
}

分页查询

MybatisPlus内置了专门用于分页的插件,使用起来非常简单,它是基于拦截器原理实现分页的

① 配置拦截器

package com.itheima.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}

② 分页代码实现

    //分页查询
    //select * from tb_user where id > 1 limit 5,7
    @Test
    public void testPage() {
        //1. 设置分页条件 当前页面 每页条数
        Page<User> page = new Page<>(2, 3);

        //2. 设置业务条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getAge, 18);

        //3. 调用分页方法
        page = userMapper.selectPage(page, wrapper);

        //4. 获取分页结果
        System.out.println("总条数:" + page.getTotal());
        System.out.println("总页数:" + page.getPages());
        System.out.println("当前页数据集合:" + page.getRecords());
    }

条件修改

// 参数1: 封装要修改的字段  参数2: 封装更新条件
int update(T entity,Wrapper<T> updateWrapper);
package com.itheima.test;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserMapperTest4 {

    @Autowired
    private UserMapper userMapper;

    //条件更新
    //update tb_user set age = 10,password = '123123' where name = 'tom'
    @Test
    public void testUpdate() {
        //1. 设置更新条件
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getName, "tom");

        //2. 设置更新字段
        User user = User.builder()
                .age(10)
                .password("123123")
                .build();

        //3. 执行更新
        userMapper.update(user, wrapper);
    }
}

条件删除

// 条件删除
int delete(Wrapper<T> wrapper);
    //条件删除
    //delete from tb_user where name = 'tom';
    @Test
    public void testDelete() {
        //1. 设置删除条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getName, "tom");

        //2. 执行删除
        userMapper.delete(wrapper);
    }

实用功能

逻辑删除

逻辑删除指的是:当执行删除操作时,并不是将对应记录真正从数据库删掉,而是使用一个标识列,将要删除的数据标识为删除状态

MP使用@TableLogic就可以轻松实现这个功能

①:在user表中添加逻辑删除标记字段,并设置默认值为0

②:实体类中添加对应字段,设定为当前字段为逻辑删除标记字段

    //逻辑删除字段:value用于指定未删除状态的值, delval用于指定删除状态的值
    @TableLogic(value = "0", delval = "1")
    private Integer deleted;

③ 删除其中一个用户观察效果

@TableLogic 只是单个表设置逻辑删除字段,如果多张表都需要配置逻辑删除,则可以做全局配置

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

自动填充

对于数据表中的一些公共字段,我们可以使用mybatisplus的自动填充功能来统一设置值

@TableField注解的fill属性可以完成这个功能 [1. 什么时候填充 2. 填充什么值]

① 在实体类的字段上添加注解

@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
描述
INSERT插入时填充字段
UPDATE更新时填充字段
INSERT_UPDATE插入和更新时填充字段

② 在配置类中设置要填充的值

package com.itheima.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    //新增时执行此方法
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime",  LocalDateTime.class, LocalDateTime.now());
    }

    //修改时执行此方法
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "updateTime",  LocalDateTime.class, LocalDateTime.now()); 
    }
}

③ 测试

    //自动填充
    @Test
    public void testFill(){
        //1. 封装user对象
        User user = User.builder()
                .username("baima10")
                .name("tom10")
                .password("123456")
                .age(18)
                .tel("13700137001")
                .build();
        //2. 调用mapper方法
        userMapper.insert(user);
    }

多余属性

多余属性指的是:实体类中存在,但是在数据表没有对应字段的属性

此时需要使用@TableField(exist = false)标识此属性

package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
//标注在实体类上,用于声明实体类对应的表,如果表名和类名一致可以省略
//@TableName("tb_user")
public class User {

    //type: 声明主键生成策略
    @TableId(type = IdType.AUTO)
    private Long id;
    private String username;
    private String name;
    private String password;

    @TableField(exist = false)//表示当前属性仅在实体类中存在,在数据表中没有对应的字段
    private String password2;

    private Integer age;
    private String tel;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    //逻辑删除字段:value用于指定未删除状态的值, delval用于指定删除状态的值
    //@TableLogic(value = "1", delval = "0")
    private Integer deleted;
}

综合案例

  1. 添加页面
  2. 运行程序
  3. 访问页面

UserController

package com.itheima.controller;

import com.itheima.domain.User;
import com.itheima.service.UserService;
import com.itheima.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user")
    public Result findList(){
        List<User> userList = userService.findList();
        return Result.success(userList);
    }

    @PostMapping("/user")
    public Result save(@RequestBody User user){
        userService.save(user);
        return Result.success();
    }

    @DeleteMapping("/user/{id}")
    public Result deleteById(@PathVariable("id") Long id){
        userService.deleteById(id);
        return Result.success();
    }

    @GetMapping("/user/{id}")
    public Result findById(@PathVariable("id") Long id){
        User user = userService.findById(id);
        return Result.success(user);
    }

    @PutMapping("/user")
    public Result update(@RequestBody User user){
        userService.update(user);
        return Result.success();
    }
}

UserService

package com.itheima.service;

import com.itheima.domain.User;

import java.util.List;

public interface UserService {

    List<User> findList();

    void save(User user);

    void deleteById(Long id);

    User findById(Long id);

    void update(User user);
}

UserServiceImpl

package com.itheima.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> findList() {
        return userMapper.selectList(new LambdaQueryWrapper<>());
    }

    @Override
    public void save(User user) {
        userMapper.insert(user);
    }

    @Override
    public void deleteById(Long id) {
        userMapper.deleteById(id);
    }

    @Override
    public User findById(Long id) {
        return userMapper.selectById(id);
    }

    @Override
    public void update(User user) {
        userMapper.updateById(user);
    }
}

ID精度损失问题

后端返回一个Long的id时候,前端接收到的数据精度有损失

pom.xml

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.13.0</version>
        </dependency>

WebMvcConfig

package com.itheima.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.math.BigInteger;
import java.util.List;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    //扩展mvc框架的消息转换器
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        converters.add(0, messageConverter);
    }
    
    /**
     * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
     * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
     * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
     */
    public class JacksonObjectMapper extends ObjectMapper {
        public JacksonObjectMapper() {
            super();

            //对应JDK8+ 时间类型处理需要添加的模块
            this.registerModule(new JavaTimeModule());

            //收到未知属性时不报异常
            this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

            //反序列化时,属性不存在的兼容处理
            this.getDeserializationConfig().withoutFeatures(FAIL_ON_UNKNOWN_PROPERTIES);

            //自定义转换规则
            SimpleModule simpleModule = new SimpleModule()
                    .addSerializer(BigInteger.class, ToStringSerializer.instance)//将BigInteger转换为String
                    .addSerializer(Long.class, ToStringSerializer.instance);//将Long转换成String
            this.registerModule(simpleModule);
        }
    }
}

Service接口

介绍

为了简化service代码编写,mybatisPlus也提供了的基础接口和实现类

我们只需要让我们自己的service去继承它提供的service,就可以使用里面的方法

进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆

分类方法描述
新增boolean save(T entity)新增,entity 实体对象
boolean saveOrUpdate(T entity)id存在则更新记录,否插入一条记录
boolean saveBatch(Collection entityList)插入(批量),默认一次可以保存1000条数据
修改boolean updateById(T entity)根据 ID 修改
boolean update(T entity,Wrapper updateWrapper)根据 条件 修改
查询T getById(Serializable id)根据 ID 查询
List listByIds(Collection idList) 查询(根据ID 批量查询)
List list() 查询所有
List list(Wrapper queryWrapper)条件查询
删除boolean removeById(Serializable id) 根据 ID 删除
boolean removeByIds(Collection idList) 删除(根据ID 批量删除)
boolean remove(Wrapper queryWrapper) 根据条件删除

修改Service

使用Service 接口使用

  • 接口继承 IService
  • 实现类继承 ServiceImpl<M,T>

UserService

package com.itheima.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.domain.User;

//自定义service接口
public interface UserService extends IService<User> {

}

UserServiceImpl

package com.itheima.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;

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

}

修改controller

package com.itheima.controller;

import com.itheima.domain.User;
import com.itheima.service.UserService;
import com.itheima.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user")
    public Result findList(){
        List<User> userList = userService.list();
        return Result.success(userList);
    }

    @PostMapping("/user")
    public Result save(@RequestBody User user){
        userService.saveOrUpdate(user);
        return Result.success();
    }

    @DeleteMapping("/user/{id}")
    public Result deleteById(@PathVariable("id") Long id){
        userService.removeById(id);
        return Result.success();
    }

    @GetMapping("/user/{id}")
    public Result findById(@PathVariable("id") Long id){
        User user = userService.getById(id);
        return Result.success(user);
    }

    @PutMapping("/user")
    public Result update(@RequestBody User user){
        userService.saveOrUpdate(user);
        return Result.success();
    }
}

代码生成器

mybatis-plus官方提供了代码生成器,可以根据数据库表,逆向生成 Controller Service Mapper代码

这里使用 IDEA的插件完成代码生成

  1. 安装插件
  2. 配置数据源
  3. 代码生成

安装插件

配置数据源

jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&IntegerEncoding=utf8&useSSL=false&servertimeZone=Asia/Shanghai

代码生成

多表查询

表间关系

添加新表

CREATE TABLE `tb_account` (
  `aid` INT(11) PRIMARY KEY AUTO_INCREMENT,
  `accountName` VARCHAR(100) DEFAULT NULL,
  `balance` FLOAT(10,2) DEFAULT NULL,
  `uid` INT(11) DEFAULT NULL
);
INSERT  INTO `tb_account` VALUES 
(1,'B01',100.00,1),
(2,'B02',200.00,1),
(3,'B03',300.00,1),
(4,'B04',400.00,2);
package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * <p>
 *    账户
 * </p>
 *
 * @author gxm
 * @since 2024-08-11
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_account")
@Builder
public class Account implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "aid", type = IdType.AUTO)
    private Long aid;

    @TableField("accountName")
    private String accountName;

    @TableField("balance")
    private Float balance;

    @TableField("uid")
    private Integer uid;
}

查询所有账户, 并且带出账户的用户信息

建立关系

接口

映射

<?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.mapper.AccountMapper">

    <!--自定义结果映射-->
    <resultMap id="BaseResultMap" type="com.itheima.domain.Account">
        <result column="aid" property="aid"></result>
        <result column="accountName" property="accountName"></result>
        <result column="balance" property="balance"></result>
        <result column="uid" property="uid"></result>

        <association property="user" javaType="com.itheima.domain.User">
            <result column="id" property="id"></result>
            <result column="username" property="username"></result>
            <result column="name" property="name"></result>
            <result column="password" property="password"></result>
            <result column="age" property="age"></result>
            <result column="tel" property="tel"></result>
            <result column="create_time" property="createTime"></result>
            <result column="update_time" property="updateTime"></result>
            <result column="deleted" property="deleted"></result>
        </association>
    </resultMap>

    <!-- 通用查询映射结果 -->
    <select id="findAllWithUser" resultMap="BaseResultMap">
        select * from tb_account a left join tb_user u on a.uid = u.id
    </select>
</mapper>

测试

查询所有用户, 并且带出用户的账户信息

建立关系

接口

映射

<?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.mapper.UserMapper">

   <!-- 通用查询映射结果 -->
   <resultMap id="BaseResultMap" type="com.itheima.domain.User">
       <id column="id" property="id" />
       <result column="username" property="username" />
       <result column="name" property="name" />
       <result column="password" property="password" />
       <result column="age" property="age" />
       <result column="tel" property="tel" />
       <result column="create_time" property="createTime" />
       <result column="update_time" property="updateTime" />
       <result column="deleted" property="deleted" />

       <collection property="accountList" ofType="com.itheima.domain.Account">
           <result column="aid" property="aid" />
           <result column="accountName" property="accountName" />
           <result column="balance" property="balance" />
           <result column="uid" property="uid" />
       </collection>
   </resultMap>

   <select id="findAllWithAccount" resultMap="BaseResultMap">
       select * from tb_user u left join tb_account a on a.uid = u.id
   </select>

</mapper>

测试

接口

映射

<?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.mapper.UserMapper">

   <!-- 通用查询映射结果 -->
   <resultMap id="BaseResultMap" type="com.itheima.domain.User">
       <id column="id" property="id" />
       <result column="username" property="username" />
       <result column="name" property="name" />
       <result column="password" property="password" />
       <result column="age" property="age" />
       <result column="tel" property="tel" />
       <result column="create_time" property="createTime" />
       <result column="update_time" property="updateTime" />
       <result column="deleted" property="deleted" />

       <collection property="accountList" ofType="com.itheima.domain.Account">
           <result column="aid" property="aid" />
           <result column="accountName" property="accountName" />
           <result column="balance" property="balance" />
           <result column="uid" property="uid" />
       </collection>
   </resultMap>

   <select id="findAllWithAccount" resultMap="BaseResultMap">
       select * from tb_user u left join tb_account a on a.uid = u.id
   </select>

</mapper>

测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

啵啵薯条

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

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

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

打赏作者

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

抵扣说明:

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

余额充值