Spring学习笔记(二十七)——springboot集成MyBatis-Plus学习总结

2022-09-26 18:10:11 浏览数 (1)

什么是MyBatis-Plus

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 mybatis plus 官网:https://baomidou.com/ 使用前可以安装 MybatisX 插件提升开发效率。

MyBatis-Plus基本特性

自动配置 * MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制 * SqlSessionFactory 自动配置好。底层是容器中默认的数据源 * mapperLocations 自动配置好的。有默认值。classpath*:/mapper/**/*.xml;任意包的类路径下的所有* mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下 * 容器中也自动配置好了 SqlSessionTemplate * @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan("cn.kt.xxxx.mapper")批量扫描就行

优点:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

缺点:

  • 只支持单表的CRUD构造器操作,如果业务复杂,涉及多表联合查询,级联等操作,还是需要像MyBatis一样手动配置。
  • 只能算是Mybatis的升级版,很多其他工具的整合都是默认使用Spring Data JPA,看公司的使用习惯吧。

框架结构

整合和使用MyBatis-Plus

1. 建测试表

现有在 ceshi 的数据库下建一张 User 表,其表结构如下:

数据库脚本如下:

代码语言:javascript复制
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  `createtime` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP,
  `updatetime` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@qq.com', '2021-08-17 09:45:31', '2021-08-17 10:32:03');
INSERT INTO `user` VALUES (13, '如我西沉', 36, '2222222@qq.com', '2021-08-17 10:04:54', '2021-08-17 11:24:48');

2. 新建springboot项目、导入相关依赖

测试项目目录结构如下

相关依赖

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cn.kt</groupId>
    <artifactId>mybatisplus</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatisplus</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

说明:我们使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus! 版本的差异!

3. 配置文件application.yml

代码语言:javascript复制
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ceshi?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

#配置日志,我们所用的sql现在是不可见的,我们希望知道他是怎么执行的,所以我们必须要查看日志!
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4. 配置分页插件和自动化填充时间

MybatisPlusInterceptor:配置mybatis-plus的分页插件 新建 /config/MyBatisConfig.java

代码语言:javascript复制
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;
/**
 * @author tao
 * @date 2021-08-16 23:11
 * 概要: 配置mybatis-plus的分页插件
 */
@Configuration
public class MyBatisConfig {
    @Bean
    public MybatisPlusInterceptor paginationInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join

        //这是分页拦截器
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setOverflow(true);
        paginationInnerInterceptor.setMaxLimit(500L);
        mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);

        return mybatisPlusInterceptor;
    }
}

MetaObjectHandler:自动化填充创建时间、修改时间 创建时间、修改时间!这些个操作一般都是自动化完成,我们不希望手动更新!

方式一:数据库级别 (工作中不允许)

​在表中新增字段 create_time 、update_time,设为默认CURRENT_TIMESIAMP

方式二:代码级别 ​在表中新增字段 createtime 、updatetime: 新建 /config/MyMetaObjectHandler.java

代码语言:javascript复制
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Created by tao.
 * Date: 2021/8/17 9:53
 * 描述: 自动化填充创建时间、修改时间
 */
@Configuration
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createtime",new Date(),metaObject);
        this.setFieldValByName("updatetime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updatetime",new Date(),metaObject);
    }
}

5. 新建实体类

新建 /domain/User.java

代码语言:javascript复制
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.util.Date;

/**
 * @author tao
 * @date 2021-08-16 22:03
 * 概要:
 */
@Data
@TableName("user")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    //创建时间,插入数据时操作
    @TableField(fill = FieldFill.INSERT)
    private Date createtime;
    //更新时间,插入和更新是操作
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updatetime;

    public User(String name, Integer age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public User() {
    }
}

说明: 主键生成策略:数据库插入的id为全局默认的id(ID_WORKER),我们需要配置主键自增,在实体类字段上添加注解: @TableId(type =IdType.AUTO),数据库字段一定要是自增的。

代码语言:javascript复制
AUTO(0),   //数据可id自增
NONE(1),   //未设置主键
INPUT(2),   //手动输入
ID_WORKER(3), //默认的全局唯一id
UUID(4),  //全局唯一id uuid
ID_WORKER_STR(5); // ID_WORKEK 字符串表示法
 ```
 如果主键没有设置Id自增,则mybatis-plus默认使用雪花算法生成Id.
 >雪花算法:
SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的。
这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。

### 6. 编写dao层
使用mybatis-plus后,只需要编写一个接口继承 BaseMapper,就可以实现基本的CRUD操作。
新建 /mapper/UserMapper.java
```java
import cn.kt.mybatisplus.domain.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * @author tao
 * @date 2021-08-16 22:04
 * 概要:
 */
public interface UserMapper extends BaseMapper<User> {
}

7. 编写测试方法

主要测试

代码语言:javascript复制
* 根据Id查询
* 查询全部
* 增加一条数据
* 修改一条数据
* 根据Id删除一条数据
* 分页查询
* 根据某个字段条件查询
* 查询name不为null的用户,并且邮箱不为null的永不,年龄大于等于35的用户
* 查询年龄在10~30岁之间的用户
* 模糊查询
* 子sql查询
* 询排序:通过id进行排序

测试代码如下

代码语言:javascript复制
import cn.kt.mybatisplus.domain.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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;

/**
 * @author tao
 * @date 2021-08-16 22:14
 * 概要:
 */
@SpringBootTest
class UserMapperTest {
    @Autowired
    UserMapper userMapper;

    //根据Id查询
    @Test
    void findById() {
        User user = userMapper.selectById(1L);
        System.out.println(user.getName()   "---"   user.getEmail());

    }

    //查询全部
    @Test
    void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

    //增加一条数据
    @Test
    void testSave() {
        User nick = new User("Nick", 21, "12055@qq.com");
        userMapper.insert(nick);
    }

    //修改一条数据
    @Test
    void testUpdate() {
        User nick = new User("如我西沉", 21, "12055@qq.com");
        nick.setId(13L);
        userMapper.updateById(nick);
    }

    //根据Id删除一条数据
    @Test
    void testDelete() {
        userMapper.deleteById(1L);
    }

    //分页查询
    @Test
    void findByPage() {
        Page<User> page = new Page<>(1, 3);
        Page<User> userListPage = userMapper.selectPage(page, null);
        System.out.println("当前页号:"   userListPage.getCurrent());
        System.out.println("每页多少条数据:"   userListPage.getSize());
        System.out.println("有多少页:"   userListPage.getPages());
        System.out.println("有多少条记录:"   userListPage.getTotal());
        System.out.println("=========当前页的数据=========");
        userListPage.getRecords().forEach(System.out::println);
    }

    //根据某个字段条件查询
    @Test
    void findByName() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", "Jack");
        User user = userMapper.selectOne(wrapper);
        System.out.println(user);
    }

    // 查询name不为null的用户,并且邮箱不为null的永不,年龄大于等于35的用户
    @Test
    void findByAgeLessThan() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.isNotNull("name");
        wrapper.isNotNull("email");
        wrapper.ge("age", 35);
        userMapper.selectList(wrapper).forEach(System.out::println);
    }


        // 查询年龄在10~30岁之间的用户
    @Test
    void findByAgeBetween() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age", 20, 30);
        Integer count = userMapper.selectCount(wrapper);//查询结果数
        System.out.println(count);
    }

    //模糊查询
    @Test
    void findByLink() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();

        wrapper.notLike("name", "ll");//相当于NOT LIKE '%Z%'
        wrapper.likeLeft("email", "@qq.com");//相当于LIKE '%@qq.com'
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);//查询结果数
        maps.forEach(System.out::println);
    }

    //子查询
    @Test
    void findByWrapperId() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.inSql("id", "select id from user where id<4");
        List<User> users = userMapper.selectList(wrapper);
        System.out.println("===================================");
        users.forEach(System.out::println);
        List<Object> objects = userMapper.selectObjs(wrapper);
        objects.forEach(System.out::println);
        System.out.println("===================================");

    }

    //查询排序:通过id进行排序
    @Test
    void findByIdOrderByIdAsc() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.orderByAsc("age");
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

}

全部测试成功,测试结果如下:

8. Service层的简化操作

mybatis-plus在service层也做了相应的简化操作:自动化配置单表的CRUD,在service层也可以不写代码,但是如果需要逻辑操作,则需要自己重写接口。 * UserService接口继承 mybatis-plus的IService接口 * 在UserServiceImpl实现类中继承ServiceImpl,并且配置相应的mapper和实体类则可以让Service也具有相应的CRUD功能。

UserService.java

代码语言:javascript复制
import cn.kt.mybatisplus.domain.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 *  Service 的CRUD也不用写了
 */
public interface UserService extends IService<User> {
}

UserServiceImpl.java

代码语言:javascript复制
import cn.kt.mybatisplus.domain.User;
import cn.kt.mybatisplus.mapper.UserMapper;
import cn.kt.mybatisplus.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * @author tao
 * @date 2021-08-16 22:33
 * 概要:
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

测试代码如下

代码语言:javascript复制
import cn.kt.mybatisplus.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

/**
 * Created by tao.
 * Date: 2021/8/17 11:10
 * 描述:
 */
@SpringBootTest
class UserServiceTest {

    @Autowired
    UserService userService;

    @Test
    void findAll() {
        List<User> list = userService.list();
        list.forEach(System.out::println);
    }

    @Test
    void findOne() {
        User user = userService.getById(13L);
        System.out.println(user);
    }

    @Test
    void save() {
        User nick = new User("Nick", 33, "111111@qq.com");
        userService.save(nick);
    }

    @Test
    void update() {
        User user = userService.getById(13L);
        user.setEmail("2222222@qq.com");
        userService.updateById(user);
    }

    @Test
    void delete() {
        boolean isDelete = userService.removeById(14L);
        System.out.println(isDelete);
    }
}

总结

mybatis plus极大的增强了开发效率,在某些复杂的SQL操作中,也可以和mybatis一样,灵活的使用配置xml或者使用注解。具体详情可以参考我之前的文章:MyBatis多表查询详解

0 人点赞