SpringBoot整合MyBatis-Puls

数据库准备

1.先准备MySQL的数据库表user

CREATE TABLE user
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
	age INT(11) NULL DEFAULT NULL COMMENT '年龄',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY (id)
);

2.插入测试数据

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');

JAVA代码准备
1.在创建时可以直接选上mysql+web模块,项目初始化完后,创建表对应的实体类User

package com.tao.mybatis.pojo;

import lombok.Data;

@Data//这个是lombok插件,需要下载插件和导入依赖
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

2.创建mapper接口

package com.tao.mybatis.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tao.mybatis.pojo.User;

public interface UserMapper extends BaseMapper<User> {
}

3.在主启动类上添加包扫描路径-mapper的包路径

package com.tao.mybatis;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan(basePackages = {"com.tao.mybatis.mapper"})
@SpringBootApplication
public class MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisApplication.class, args);
    }
}

4.配置properties数据库驱动+链接

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?allowMultiQueries=true&serverTimezone=UTC&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

项目结构图
SpringBoot整合MyBatis-Puls_第1张图片

5.编写测试代码

package com.tao.mybatis;
import com.tao.mybatis.mapper.UserMapper;
import com.tao.mybatis.pojo.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;
@SpringBootTest
class MybatisApplicationTests {

    @Autowired
    private UserMapper userMapper;

    //查询user表所有数据
    @Test
    public void findAll() {
        //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
        //所以不填写就是无任何条件
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    }
}

到这一步的代码提交版本:初始化基本代码

测试结果
SpringBoot整合MyBatis-Puls_第2张图片
注意:
IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
为了避免报错,可以在 dao 层 的接口上添加 @Repository 注

6.接下来基本的CURD来一套

  //添加操作
    @Test
    public void addUser() {
        User user = new User();
        user.setName("111");
        user.setAge(70);
        user.setEmail("[email protected]");
        int insert = userMapper.insert(user);
        System.out.println("insert:"+insert);
    }
	//删除操作 物理删除
    @Test
    public void testDeleteById(){
        int result = userMapper.deleteById(1231125349744828417L);
        System.out.println(result);
    }
	//修改操作
    @Test
    public void updateUser() {
        User user = new User();
        user.setId(1);//数据库中的id
        user.setAge(120);
        int row = userMapper.updateById(user);
        System.out.println(row);
    }
    @Test
    public void testSelectByMap(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "Jone");
        map.put("age", 18);
        List<User> users = userMapper.selectByMap(map);

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

7.添加SQL请求日志

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

或者使用调整SpringBoot的日志级别

logging:
  level:
    com.atguigu.gulimall: debug

显示效果(主要是开发环境查看SQL打印情况)
SpringBoot整合MyBatis-Puls_第3张图片
在插入语句的时候查看数据不会发现他的ID好像怪怪的,这是MP的主键默认生成策略导致的

主键生成策略

1.ID_WORKER
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID
参考资料:分布式系统唯一ID生成方案汇总:访问地址 也就是我们什么都不写插入一条数据MySQL中看到的ID;
@TableId(type = IdType.ID_WORKER) //mp自带策略,生成19位值,数字类型使用这种策略,比如long
@TableId(type = IdType.ID_WORKER_STR) //mp自带策略,生成19位值,字符串类型使用这种策略

2.自增策略–需要改表,主键改为自增

  1. 需要在创建数据表的时候设置主键自增
  2. 实体字段中配置 @TableId(type = IdType.AUTO)
 /**
     * 数据库ID自增
     */
    AUTO(0),
    /**
     * 该类型为未设置主键类型
     */
    NONE(1),
    /**
     * 用户输入ID
     * 该类型可以通过自己注册自动填充插件进行填充
     */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 全局唯一ID (idWorker)
     */
    ID_WORKER(3),
    /**
     * 全局唯一ID (UUID)
     */
    UUID(4),
    /**
     * 字符串全局唯一ID (idWorker 的字符串表示)
     */
    ID_WORKER_STR(5);

自动填充

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:

1.数据库表中添加自动填充字段
在User表中添加datetime类型的新的字段 create_time、update_time

2.实体上添加注解

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

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

3.实现元对象处理器接口

package com.tao.mybatis.config;

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

import java.util.Date;

/**
* @description: 自动填充
* @author TAO
* @date 2020/5/24 17:29
*/
@Component
public class MybatisPulsAutoFill implements MetaObjectHandler {
    //使用mp实现添加操作,这个方法执行
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    //使用mp实现修改操作,这个方法执行
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }


}

4.调用原有的插入测试代码
SpringBoot整合MyBatis-Puls_第4张图片

MP实现乐观锁

简单介绍一下乐观锁,乐观锁本身不是问题,是解决问题产生的一种解决方案,乐观锁主要解决丢失更新的问题,这谈到丢失更新就会涉及到数据库事务, 事务就是逻辑上的操作要么都成功要么一起失败!

如果考虑事务的隔离性那么就会产生脏读,幻读/虚读,不可重复读;但这些都是读的问题,还有写的问题,丢失更新,那么乐观锁就是解决写的丢失问题,这个问题的产生是在并发写数据的时候才会产生,说白了就是多人操作同一条数据库记录。

举个例子,数据库中有一条记录,记录中有一个字段数据为100,这时A来修改这条记录,将100修改为800,这时数据还为提交,这时B也来了,也恰巧在操作这条记录,他是将100修改为200,这是A事务提交完成,B在A的后面完成事务提交,那么看似没什么问题,但是B的修改操作是直接将A的操作给覆盖掉,那也就是说A无论将值改成什么都将会被B的修改,这种现象就叫丢失更新问题,也就是A的更新数据会丢失,这个问题就可以使用乐观锁来解决;

这里补充一点:
悲观锁:A在操作数据的时候,B是不能操作这个数据的,只能排着队操作数据(串行化)效率低
乐观锁:

乐观锁的实现原理

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败

上代码测一下效果
1.添加数据库字段

ALTER TABLE `user` ADD COLUMN `version` INT

2.用户实体类添加属性

	@Version//乐观锁关键起作用的
    @TableField(fill = FieldFill.INSERT)//这只是让version创建记录是有个默认版本而已
    private Integer version;//版本号

3.添加自动填充insertFill中插入时version的默认数据

 this.setFieldValByName("version", 1, metaObject);//这只是让version创建记录是有个默认版本而已

4.添加MyBatis-Puls乐观锁配置

package com.tao.mybatis.config;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @description: Mybatis-Push核心配置
* @author TAO
* @date 2020/5/25 14:59
*/
@Configuration
public class MyBatisPulsConfig {
    //乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

5.运行测试案例中的插入方法addUser新加入一条记录AAA
SpringBoot整合MyBatis-Puls_第5张图片

6.编写测试代码

    //测试 乐观锁插件
    @Test
    public void testOptimisticLocker() {
        //查询
        User user = userMapper.selectById("1264819379007172609");//这里的id是数据库里的
        //修改数据
        user.setName("CCC");
        //执行更新
        userMapper.updateById(user);
    }

查看Mysql数据结果name字段修改了,version的版本自动也添加了
SpringBoot整合MyBatis-Puls_第6张图片

MP实现分页

1.Mybatis-Push核心配置添加分页插件

	//分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

2.测试代码添加分页请求

//分页查询
    @Test
    public void testPage() {
        //1 创建page对象
        //传入两个参数:当前页 和 每页显示记录数
        Page<User> page = new Page<>(1,3);
        //调用mp分页查询的方法
        //调用mp分页查询过程中,底层封装
        //把分页所有数据封装到page对象里面
        userMapper.selectPage(page,null);

        //通过page对象获取分页数据
        System.out.println(page.getCurrent());//当前页
        System.out.println(page.getRecords());//每页数据list集合
        System.out.println(page.getSize());//每页显示记录数
        System.out.println(page.getTotal()); //总记录数
        System.out.println(page.getPages()); //总页数

        System.out.println(page.hasNext()); //下一页
        System.out.println(page.hasPrevious()); //上一页
    }

3.打印结果

SpringBoot整合MyBatis-Puls_第7张图片

逻辑删除

在车上代码中加入这个删除方法,删除一条数据

	//删除操作 物理删除
    @Test
    public void testDeleteById(){
        int result = userMapper.deleteById(1264819379007172609L);
        System.out.println(result);
    }

1.添加数据库删除标记字段

ALTER TABLE `user` ADD COLUMN `deleted` boolean

2.User实体类中添加标记字段

@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;

3.添加自动填充insertFill中插入时deleted的删除标识

this.setFieldValByName("deleted", 0, metaObject);

4.Mybatis-Push核心配置添加逻辑删除

 	//逻辑删除,注意官方之初,MP3.1.1以后这段配置可以不需要配置了
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }

5.properties配置文件

#删除后的值
mybatis-plus.global-config.db-config.logic-delete-value=1
#默认的值-和数据库中的删除标识值的默认值一样
mybatis-plus.global-config.db-config.logic-not-delete-value=0
mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1
      logic-not-delete-value: 0

6.再次调用删除测试代码
SpringBoot整合MyBatis-Puls_第8张图片
这里发现这条数据并没有被真实删除,不像开始的删除一样。这里是直接将删除标识改成了1,也就是我们porperties配置文件中添加的配置一样,然后我们调用查询所有记录的方法!

7.调用findAll查看效果
SpringBoot整合MyBatis-Puls_第9张图片
不难发现CCC用户确实不在了,这里发送的逻辑删除实际上不是发送的delete语句,而是update语句,然后查询的时候也会根据配置代码带上删除标识的值来查询!

性能分析插件

1.添加插件配置

	//SQL 执行性能分析插件
    //开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长

    //dev开发  test测试  prod生产
    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }

2.切换项目环境properties

#环境设置:dev、test、prod
spring.profiles.active=dev

3.启动添加测试
SpringBoot整合MyBatis-Puls_第10张图片
上面的是sql日志,下面的是sql性能分析

MP条件构造器

SpringBoot整合MyBatis-Puls_第11张图片

  1. Wrapper : 条件构造抽象类,最顶端父类
  2. AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
  3. QueryWrapper : Entity 对象封装操作类,不是用lambda语法
  4. UpdateWrapper : Update 条件封装,用于Entity对象更新操作
  5. AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
  6. LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
  7. LambdaUpdateWrapper : Lambda 更新封装Wrapper

1、ge、gt、le、lt、isNull、isNotNull

@Test
public void testDelete() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper
        .isNull("name")
        .ge("age", 12)
        .isNotNull("email");
    int result = userMapper.delete(queryWrapper);
    System.out.println("delete return count = " + result);
}
SQLUPDATE user SET deleted=1 WHERE deleted=0 AND name IS NULL AND age >= ? AND email IS NOT NULL

2、eq、ne
注意:seletOne返回的是一条实体记录,当出现多条时会报错

@Test
public void testSelectOne() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("name", "Tom");

    User user = userMapper.selectOne(queryWrapper);
    System.out.println(user);
}
SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ? 

3、between、notBetween–包含大小边界

@Test
public void testSelectCount() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.between("age", 20, 30);

    Integer count = userMapper.selectCount(queryWrapper);
    System.out.println(count);
}
SELECT COUNT(1) FROM user WHERE deleted=0 AND age BETWEEN ? AND ? 

4、allEq

@Test
public void testSelectList() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    Map<String, Object> map = new HashMap<>();
    map.put("id", 2);
    map.put("name", "Jack");
    map.put("age", 20);

    queryWrapper.allEq(map);
    List<User> users = userMapper.selectList(queryWrapper);

    users.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version 
FROM user WHERE deleted=0 AND name = ? AND id = ? AND age = ? 

5、like、notLike、likeLeft、likeRight
selectMaps返回Map集合列表

@Test
public void testSelectMaps() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper
        .notLike("name", "e")
        .likeRight("email", "t");

    List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
    maps.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version 
FROM user WHERE deleted=0 AND name NOT LIKE ? AND email LIKE ? 

6、in、notIn、inSql、notinSql、exists、notExists
in、notIn:

  1. notIn(“age”,{1,2,3})—>age not in (1,2,3)
  2. notIn(“age”, 1, 2, 3)—>age not in (1,2,3)
    inSql、notinSql:可以实现子查询
  3. inSql(“age”, “1,2,3,4,5,6”)—>age in (1,2,3,4,5,6)
  4. inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3)
@Test
public void testSelectObjs() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    //queryWrapper.in("id", 1, 2, 3);
    queryWrapper.inSql("id", "select id from user where id < 3");

    List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
    objects.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version 
FROM user WHERE deleted=0 AND id IN (select id from user where id < 3) 

7、or、and
注意:这里使用的是 UpdateWrapper
不调用or则默认为使用 and 连

@Test
public void testUpdate1() {

    //修改值
    User user = new User();
    user.setAge(99);
    user.setName("Andy");

    //修改条件
    UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
    userUpdateWrapper
        .like("name", "h")
        .or()
        .between("age", 20, 30);

    int result = userMapper.update(user, userUpdateWrapper);

    System.out.println(result);
}
UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ? OR age BETWEEN ? AND ?

8、嵌套or、嵌套and
这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号

@Test
public void testUpdate2() {


    //修改值
    User user = new User();
    user.setAge(99);
    user.setName("Andy");

    //修改条件
    UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
    userUpdateWrapper
        .like("name", "h")
        .or(i -> i.eq("name", "李白").ne("age", 20));

    int result = userMapper.update(user, userUpdateWrapper);

    System.out.println(result);
}
UPDATE user SET name=?, age=?, update_time=? 
WHERE deleted=0 AND name LIKE ? 
OR ( name = ? AND age <> ? ) 

9、orderBy、orderByDesc、orderByAsc

@Test
public void testSelectListOrderBy() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderByDesc("id");

    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version 
FROM user WHERE deleted=0 ORDER BY id DESC 

10、last
直接拼接到 sql 的最后
注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

@Test
public void testSelectListLast() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.last("limit 1");

    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version 
FROM user WHERE deleted=0 limit 1 

11、指定要查询的列

@Test
public void testSelectListColumn() {

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.select("id", "name", "age");

    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}
SELECT id,name,age FROM user WHERE deleted=0 

12、set、setSql
最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段

@Test
public void testUpdateSet() {

    //修改值
    User user = new User();
    user.setAge(99);

    //修改条件
    UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
    userUpdateWrapper
        .like("name", "h")
        .set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
        .setSql(" email = '[email protected]'");//可以有子查询

    int result = userMapper.update(user, userUpdateWrapper);
}
UPDATE user SET age=?, update_time=?, name=?, email = '[email protected]' WHERE deleted=0 AND name LIKE ? 

至于一些其他复杂的高级SQL语句,建议使用mapper映射文件的方式来写;

你可能感兴趣的:(JAVA,Spring)