MyBatis-Plus

前言

使用Mybatis进行开发有以下不足

1.每一张表都需要配置一套基本的增删改查功能,造成代码重复;

3.所有SQL语句全部自己写,表字段名称容易拼写错误;

2.使用xml标签实现动态SQL配置起来比较复杂;

MyBatis-Plus(简称MP)是Mybatis的增强版,MyBatis-Plus在Mybatis的基础上只做增强不做改变,MyBatis-Plus为简化配置、高效率实现持久层而生;

MyBatis-Plus封装好了大量增删改查的方法,程序员只需要继承BaseMapper接口即可以使用这些方法,无需再重复开发;

MyBatis-Plus有以下特性

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:88f60ca6-ce6f-4b9a-a01d-4f7cfe319678

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:9e715c1e-b234-4880-8680-2b6a8ce858bd

* 强大的CURD操作:MyBatis-Plus内置了通用Mapper,少量配置即可实现单表的增伤改查
* 支持Lambda表达式:编写查询条件无需担心字段拼写错误
* 支持主键字段自动生成
* 内置分页插件

二、入门案例

在数据库中insert 1条记录;

1.数据库环境准备

在数据库中创建1张user表;

MyBatis-PlusMyBatis-Plus
CREATE TABLE user (
    id bigint(20) primary key auto_increment,
    name varchar(32) not null,
    password  varchar(32) not null,
    age int(3) not null ,
    tel varchar(32) not null
);
insert into user values(null,'岳飞','123456',12,'12345678910');
insert into user values(null,'逍遥王','123456',8,'12345667910');
insert into user values(null,'易云','123456',15,'12345678910');
insert into user values(null,'张弢','123456',9,'12345678910');
insert into user values(null,'少林圣僧','123456',28,'12345678910');
insert into user values(null,'张启樵','123456',22,'12345678910');
insert into user values(null,'奔雷','123456',16,'12345018910');
insert into user values(null,'闪电','123456',16,'123908910');
insert into user values(null,'宋远桥','123456',16,'1224350178910');

user.sql

2. 创建SpringBoot项目

创建SpringBoot项目,导入Mybatis-Plus依赖;

MyBatis-PlusMyBatis-Plus
    <dependencies>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.6version>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.15version>
        dependency>
    <span>&lt;</span><span>dependency</span><span>&gt;</span>
        <span>&lt;</span><span>groupId</span><span>&gt;</span>org.projectlombok<span><span>groupId</span><span>&gt;</span>
        <span>&lt;</span><span>artifactId</span><span>&gt;</span>lombok<span><span>artifactId</span><span>&gt;</span>
        <span>&lt;</span><span>version</span><span>&gt;</span>1.18.8<span><span>version</span><span>&gt;</span>
    <span><span>dependency</span><span>&gt;</span>

    <span>&lt;</span><span>dependency</span><span>&gt;</span>
        <span>&lt;</span><span>groupId</span><span>&gt;</span>org.springframework.boot<span><span>groupId</span><span>&gt;</span>
        <span>&lt;</span><span>artifactId</span><span>&gt;</span>spring-boot-starter-test<span><span>artifactId</span><span>&gt;</span>
    <span><span>dependency</span><span>&gt;</span>

    <span>&lt;</span><span>dependency</span><span>&gt;</span>
        <span>&lt;</span><span>groupId</span><span>&gt;</span>com.baomidou<span><span>groupId</span><span>&gt;</span>
        <span>&lt;</span><span>artifactId</span><span>&gt;</span>mybatis-plus-boot-starter<span><span>artifactId</span><span>&gt;</span>
        <span>&lt;</span><span>version</span><span>&gt;</span>3.4.0<span><span>version</span><span>&gt;</span>
    <span><span>dependency</span><span>&gt;</span>

<span><span>dependencies</span><span>&gt;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>pom.xml

3.创建实体类

数据库中有1张表就需要1个实体类与之对应;

@TableName("user"):此注解:用于指定当前实体类关联到数据库中哪1张表, 省略此注解后,为类名首字母小写;

@TableField("id"): 此注解:用于指定当前实体类字段关联到数据库表中哪1个字段,省略此注解后,当前属性名称和数据库表中字段名称一致;

MyBatis-PlusMyBatis-Plus
package com.gen.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.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor

//此注解:用于绑定当前实体类关联到MySQL中哪一张表,省略此注解后,为类名首字母小写
@TableName("user")
public class User {
    // 此注解:用于绑定当前实体类字段关联到数据库表中哪个字段,省略此注解后,当前字段和数据库表中字段名称一致
    @TableField("id")
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

User.java

4.创建映射接口

当前接口继承BaseMapper之后拥有BaseMapper接口中基本的CURD方法;

MyBatis-PlusMyBatis-Plus
package com.gen.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;

//当前接口继承.BaseMapper之后拥有BaseMapper接口中基本的CURD方法
@Repository
public interface UserMapper extends BaseMapper {
    //如果继承自父接口的方法,无法满足当前业务需求,可以对父接口的方法进行重写
}

UserMapper.interface

5.添加SpringBoot配置文件

在resources中添加SpringBoot的配置文件-application.yaml,然后在里面加入下面配置;

MyBatis-PlusMyBatis-Plus
spring:
  datasource: # 数据源
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.56.18:3306/reggie?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: zhanggen
    password: 123.com
    type: com.alibaba.druid.pool.DruidDataSource

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

application.yaml

6.创建SpringBoot启动类

MyBatis-PlusMyBatis-Plus
package com.gen;

import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
//设置扫描的mapper包
@MapperScan("com.gen.mapper")
public class MybatisPlusApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
        System.out.println("项目启动成功");
    }

}

MybatisPlusApplication.java

7.MybatisPlus主键自动生成

@TableId(type = IdType.INPUT)注解可以帮助我们自动生成1个唯一的主键;

MybatisPlus的主键生成策略有4种:

  • AUTO 数据库ID自增
  • INPUT 用户输入ID
  • ASSIGN_ID 雪花算法生成ID(默认值)
  • ASSIGN_UUID UUID算法生成ID
/**
     * Mybatis支持的主键生成策略
     * AUTO  数据库ID自增
     * INPUT 用户输入ID
     * ASSIGN_ID 雪花算法生成ID(默认值)
     * ASSIGN_UUID UUID算法生成ID
     */
    @TableId(type= IdType.AUTO)

8.测试

MyBatis-PlusMyBatis-Plus
package com.gen;

import com.gen.domain.User;
import com.gen.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    //测试保存
    @Test
    public void testInsert() {
        User user = new User();
        //user.setId(10L);//手动设置了id,用设置的
        user.setId(null);//不设置id,执行mybatis的主键策略
        user.setName("张三丰");
        user.setPassword("admin");
        user.setAge(20);
        user.setTel("13700137001");
        //保存
        userMapper.insert(user);
    }
}

UserMapperTest.java

三、基本CURD操作

当maaper接口继承了MybatisPlus提供BaseMapper接口之后,就拥有了单表的增删改查的功能,程序员可以直接使用;

  • 插入一条记录:int insert(T entity);
  • 主键查询: T selectById(Serializable id);
  • 主键批量查询:List
  • 根据ID更新: int updateById(T entity);
  • 根据ID删除: int deleteById(Serializable id);
  • 根据ID批量删除:int deleteBatchIds(Collection idList);/

测试代码

MyBatis-PlusMyBatis-Plus
package com.gen;

import com.gen.domain.User;
import com.gen.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    //测试保存
    @Test
    public void testInsert() {
        User user = new User();
        //user.setId(10L);//手动设置了id,用设置的
        user.setId(null);//不设置id,执行mybatis的主键策略
        user.setName("张三丰");
        user.setPassword("admin");
        user.setAge(20);
        user.setTel("13700137001");
        //保存
        userMapper.insert(user);
    }

    //根据ID查询1条记录
    @Test
    public void testSelectById(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
    //根据ID批量查询
    @Test
    public void selectBatchIds(){
        List idList=new ArrayList<>();
        idList.add(1L);
        idList.add(2L);
        idList.add(3L);
        List userList = userMapper.selectBatchIds(idList);
        for (User user : userList) {
            System.out.println(user);
        }

    }
    //根据ID更新1条记录
    @Test
    public void testUpdateById(){
        User newUser=new User();
        newUser.setId(10L);
        newUser.setName("鹏举");
        newUser.setAge(899);
        userMapper.updateById(newUser);
    }

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

    //根据id批量删除
    @Test
    public void testDeleteBatchIds(){
        List ids = new ArrayList<>();
        ids.add(1531089191868391425L);
        ids.add(1531090450880974849L);
        ids.add(1531090450880974850L);
        userMapper.deleteBatchIds(ids);
    }
}

UserMapperTest.java

四、复杂操作

当maaper接口继承了MybatisPlus提供BaseMapper接口之后,就拥有了单表的增删改查的功能;

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:0478bb53-3b3a-444c-931b-4ff5fa0caf13

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:a86297c0-8df7-475f-9e85-d871c13b75d6

我们可以通过QueryWrapper和LambdaQueryWrappe条件构造对象,构造各种查询条件,这个2种条件构造器都支持3个参数;

  • condition:判断条件
  • column:数据库字段
  • val:字段的值

然后再把Wrapper对象作为参数传到BaseMapper接口的接口方法中,实现条件、分页、排序、分组、过滤查询等;

BaseMapper接口中定义的接口方法有如下:

  • 条件查询,返回值为多条记录:List
  • 条件查询,返回值为1条记录: T selectOne(Wrapper
  • 条件删除:int delete(Wrapper
  • 条件更新:int update(T entity,Wrapper

1.QueryWrapper组装查询条件

参数1:判断条件,条件为真后两个参数才生效

参数2:数据库表字段名称的字符串,容易写错(硬编码)

参数3:前端传到后台的参数

//1:QueryWrapper构造查询条件
            QueryWrapper dishFlavorQueryWrapper = new QueryWrapper<>();
            dishFlavorQueryWrapper.eq(dish.getId() != null, "dish_id", dish.getId());

2.LambdaQueryWrapper组装查询条件

参数1:判断条件,条件为真后两个参数才生效

参数2:实体类中的属性名称,支持Lambada拼写,不容易写错(软编码)

参数3:前端传到后台的参数

//2:LambdaQueryWrapper构造查询条件
            LambdaQueryWrapper dishFlavorQueryWrapper = new LambdaQueryWrapper<>();
            dishFlavorQueryWrapper.eq(dish.getId() != null, DishFlavor::getDishId, dish.getId());
            dishFlavorMapper.delete(dishFlavorQueryWrapper);

3.联合条件组装

查询方法说明例子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 = '老王'

1.根据条件判断,生成多查询条件

LambdaQueryWrapper cartQueryWrapper = new LambdaQueryWrapper<>();
        cartQueryWrapper.eq(cart.getDishId() != null, Cart::getDishId, cart.getDishId());
        cartQueryWrapper.eq(cart.getSetmealId() != null, Cart::getSetmealId, cart.getSetmealId());
        cartQueryWrapper.eq(Cart::getUserId, UserHolder.get().getId());

2.使用or连接多个查询条件

//组装对个查询条件组装--链式编程
        LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.ge(User::getId, iDParam)
                .or()
                .between(User::getAge, ageParam, ageParam + 20)
                .like(User::getName, nameParam)
                .in(User::getTel, Arrays.asList(telList));
        userMapper.selectList(userLambdaQueryWrapper);

4.投影、排序、分组、过滤

4.1.投影

查询投影主要用于控制返回哪些列的数据,而不是select * from table;

//查询投影:SELECT age,id FROM user
    @Test
    public void testSelect() {
        LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.select(User::getAge,User::getId);
        List userList = userMapper.selectList(userLambdaQueryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }

    }

4.2.查询排序

//查询排序:正序:orderByAsc  倒序:orderByDes
    @Test
    public void testSelect() {
        LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 根据年龄排序:
        userLambdaQueryWrapper.orderByDesc(User::getAge);
        List userList = userMapper.selectList(userLambdaQueryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }

    }

4.3.分组和过滤

//查询分组和过滤:select name,count(1) as count from user group by name having  count>0;
    @Test
    public void testGroupAndHaving() {
        //分组过滤只能使用QueryWrapper,不能使用LambdaQueryWrapper
        QueryWrapper userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper
                .select("name", "count(1) as count")
                .groupBy("name")
                .having("count>0");
        List> list = userMapper.selectMaps(userQueryWrapper);
        //[ {name=张三, count=1}, {name=李四, count=1} ]
        System.out.println(list);

    }

5.单独定义SQL

如果继承自BaseMapper接口的接口方法也无法满足我们的需求,我们也可以在这里mapper接口中单独定义方法;

//当前接口继承.BaseMapper之后拥有BaseMapper接口中基本的CURD方法
@Repository
public interface UserMapper extends BaseMapper {
    //如果继承自父接口的方法,无法满足当前业务需求,可以对父接口的方法进行重写
    @Select("select name,count(1) as count from user group by name having  count>0")
    List> groupByName();
}

测试

//在映射接口中自定义分组和排序方法
    @Test
    public void testGroupAndHaving1() {
        List> list = userMapper.groupByName();
        //[ {name=奔雷, count=1}, {name=宋远桥, count=1} ]
        System.out.println(list);

    }

6.分页查询

MybatisPlus内置了专门用于分页的插件,使用起来非常简单,类似于SpringMVC的拦截器;

需要注意的是:

  • 我们自定义的SpringMVC拦截器(public class LoginCheckInterceptor implements HandlerInterceptor{} 拦截的是request和response;
  • MybatisPlus拦截器拦截的是Dao层Mybatis-Plus发给数据库的SQL语句,

1.配置分页拦截器

package com.gen.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 //3.把MybatisPlusInterceptor拦截器对象放入spring的容器中
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //1.创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加分页拦截器对象 MybatisPlusInterceptor拦截器对象中
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;

    }
}

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:20f61b49-0630-4c4c-9d89-e2fc9e96eea0

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:f13ce442-96e5-4611-8591-8f0b3253850b

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(clients = IArticleClient.class)
@MapperScan("com.leadnews.wemedia.mapper")
@EnableAsync //开启异步注解
public class WemediaApplication {
    public static void main(String[] args) {
        SpringApplication.run(WemediaApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }

2.代码实现

当使用MyBatis-Plus分页插件之后,mapper层返回的就是1个page对象,我们可以把这个page对象封装到ResultInfo对象中;

MyBatis-Plus

代码

//测试分页:
    @Test
    public void testPage() {
        //1.构造page对象: pageNumber、pageSize
        Page page = new Page<>(2, 3);
        //2.构造查询条件:
        LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //3.执行查询
        Page userPage = userMapper.selectPage(page, userLambdaQueryWrapper);
        System.out.println(userPage);
        //4. 输出结果
        System.out.println("总条数:" + page.getTotal());
        System.out.println("总页数:" + page.getPages());
        System.out.println("当前页数据:" + page.getRecords());
        System.out.println("当前页码值:" + page.getCurrent());
        System.out.println("每页显示数:" + page.getSize());
    }

五、常见注解

1.@TableField

@TableField标注在属性上,它有两个特殊用法:

  • exist: 当实体类中属性仅在实体类中存在(多表查询),在数据库中不存在时,使用@TableField(exist = false)标识
  • select:当数据表中某个字段不想被查询映射到实体类中的某个指定字段上时,使用@TableField(select = false)标识
@TableField(select = false)//标识不想将数据表中某个对应字段查询回来
    private String password;

 @TableField(exist = false)//标识当前属性在数据表中没有对应的字段
    private String password2;//新增加的属性

2.@TableLogic

逻辑删除,而是使用一个标识列,将要删除的数据标识为删除状态,本质是执行了一条update语句;

@TableLogic(value = "1", delval = "0")
    private Integer deleted;    //表示当前用户是否被删除,正常:1,被删除之后0

3.公共字段自动填充

如果数据库中多个表中都存在以下4个字段:createUser、updateUser、createTime、updateTime

每次都要单独设置实在是过于麻烦,我们可以使用MyBatis-Plus的自动代码填充来处理公共字段;

1. 设置公共字段填充时机

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:6e62d795-55dd-4baa-b107-a5e2e98fba0b

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:5304e5af-aa90-4574-81cf-10d097593ca5

//创建时间
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //更新时间
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE) //声明当前字段在什么时机进行内容填充
    private Date updateTime;
    //创建人
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    //修改人
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

2. 编写公共字段自动填充内容

package com.zhanggen.reggie.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.util.Date;

//自定义元数据对象处理器
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    //metaObject代表要填充的对象

    //插入新增(insert)操作,自动填充
    @Override
    public void insertFill(MetaObject metaObject) {
        metaObject.setValue("createTime", new Date());
        metaObject.setValue("updateTime", new Date());
        metaObject.setValue("createUser", 1L);
        metaObject.setValue("updateUser", 1L);
    }

    //更新操作(update),自动填充
    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", new Date());
        metaObject.setValue("updateUser", 1L);
    }
}

3.Service层调用

修改项目的service层调用MyBatis-Plus的方法

六、使用ThreadLocal存储Session信息

MyBatis-Plus

SpringBoot Web层基于SpringMVC,SpringMVC底层基于Servlet;

Servlet的每处理1个来自客户端的Request都使用1个不同的线程,创建1个request对象;

//1、判断登录状态,如果已登录,则直接放行
        User user = (User) request.getSession().getAttribute("SESSION_USER");

如果程序员在后台的Web或Service层,开了多个子线程,如何保证这些子线程可以安全访问父线程中的Request对象里面的数据呢?

1.ThreadLocal是什么?

ThreadLocal本质是一块Map结构的内存,和存储Session的内存数据结构一致;

和HttpSession不同的是。这块内存,可以保证多线程模式下,每1个线程之间的set的数据是相互隔离的;

ThreadLocal={

  thread_id1: {},

  thread_id2: {},

  thread_id3: {},

  }

2.ThreadLocal特性

不同线程之间数据都是隔离的,当前线程set的值,只有当前线程可以get到;

3.ThreadLocal应用场景

在Flask框架中每个用户的request对象都是由不同的线程创建的,底层就是通过ThreadLocal实现request对象的隔离;

如果每1个用户的Session信息都保存在服务器内存中 ,当用户访问量激增时,用户Session数据失效时间过长,很容易造成服务器端内存跑满;

我们可以搭配SpringMVC的拦截器执行时机+ThreadLocal+JTW,把用户session信息全部保存在ThreadLocal;

  • 在用户http请求到达SpringMVC处理器之前, 把当前用户相关信息保存在ThreadLocal中;(set)
  • 在用户hhtp请求到达SpringMVC处理器时,使用ThreadLocal中保存的用户信息,去填充数据库表中公共字段数据;(get)
  • 在用户hhtp请求离开SpringMVC处理器时,用户信息使用完了,就销毁当前用户保存在ThreadLoca中的值;(remove)

MyBatis-Plus

4. ThreadLocal的API

  • set(T value) :设置当前线程绑定的变量
  • get():获取当前线程绑定的变量
  • remove() :移除当前线程绑定的变量

5.代码实现Session信息保存到 ThreadLocal

在拦截器中,实现用户Session信息1次性保存到ThreadLocal;

1. EmployeeHolder

在reggie-common下创建com.itheima.reggie.common.EmployeeHolder工具类用于操作ThreadLocal;

MyBatis-PlusMyBatis-Plus
package com.itheima.reggie.common;

import com.itheima.reggie.domain.Employee;

//跟员工相关,操作ThreadLocal的工具类
public class EmployeeHolder {
    //声明1个ThreadLocal
    private static ThreadLocal threadLocal = new ThreadLocal();

    //存储
    public static void set(Employee employee) {
        threadLocal.set(employee);
    }

    //获取
    public static Employee get() {
        return threadLocal.get();
    }

    //删除
    public static void remove() {
        threadLocal.remove();
    }

}

EmployeeHolder.java

2.拦截器

搭配拦截器的执行时机,在用户请求到达拦截器之前设置(set)用户信息,在用户请求离开拦截器之后销毁(remove)用户信息;

MyBatis-PlusMyBatis-Plus
package com.itheima.reggie.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.reggie.common.EmployeeHolder;
import com.itheima.reggie.common.ResultInfo;
import com.itheima.reggie.domain.Employee;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Component//把拦截器对象放入spring的容器中,以供配置对象注入
public class LoginCheckInterceptor implements HandlerInterceptor {

    //在请求进入处理器之前进行拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //检查用户的session中是否有当前信息信息,以验证当前用户是否登录?
        HttpSession session = request.getSession();
        Employee currentEmployee = (Employee) session.getAttribute("SESSION_EMPLOYEE");
        if (currentEmployee == null) {
            //返回错误信息
            ResultInfo resultInfo = ResultInfo.error("NOTLOGIN");//组装对象
            //ResultInfo对象转为json
            String jsonString = new ObjectMapper().writeValueAsString(resultInfo);
            response.getWriter().write(jsonString);
            return false;
        } else {
            //获取到当前登录用户,保存到ThreadLocal
            EmployeeHolder.set(currentEmployee);
            return true;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    //请求即将离开服务器
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //删除到当前登录用户(线程),存储到ThreadLocal的信息
        EmployeeHolder.remove();
    }
}

LoginCheckInterceptor.java

3.Mapper层

在项目Mapper层创建com.itheima.reggie.config.MyMetaObjectHandler公共字段填充处理器;

当前用户请求到达SpringMVC的处理器经过Controller---->Service--->Mapper层(MyBatis-Plus);

如果需要调用MyBatis-Plus的insert/updateById()方法,触发公共字段填充处理器执行;

公共字段填充处理器,获取(get)到ThreadLocal中保存的用户信息,自动填充数据库表中公共字段;

MyBatis-PlusMyBatis-Plus
package com.itheima.reggie.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.itheima.reggie.common.EmployeeHolder;
import com.itheima.reggie.domain.Employee;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

//自定义元数据对象处理器
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    //metaObject代表要填充的对象
    //插入新增(insert)操作,自动填充
    @Override
    public void insertFill(MetaObject metaObject) {
        metaObject.setValue("createTime", new Date());
        metaObject.setValue("updateTime", new Date());
        //从ThreadLocal中获取当前用户的信息
        Employee employee = EmployeeHolder.get();
        if (employee != null) {
            metaObject.setValue("createUser", employee.getId());
            metaObject.setValue("updateUser", employee.getId());
        }
    }

    //更新操作(update),自动填充
    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", new Date());
        Employee employee = EmployeeHolder.get();
        if (employee != null) {
            metaObject.setValue("updateUser", 1L);
        }

    }
}

MyMetaObjectHandler.java

当SpringBoot项目集成MyBatis-Plus之后,之前使用MyBatis的定义的mapper接口功能不受影响(继承BaseMapper接口之后的重写);这就是MyBatis-Plus的无侵入;

1. 添加MyBatis-Plus依赖

com.baomidou
    mybatis-plus-boot-starter
    3.4.2

2. 修改application.yaml,替换MyBatis-Plus的配置

MyBatis-PlusMyBatis-Plus
server:
  port: 8080
spring:
  application:
    name: reggie-web-manage # 应用名称
  datasource: # 数据源配置
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.56.18:3306/reggie?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: zhanggen
      password: 123.com
#mybatis:
        ##  configuration:
        ##    map-underscore-to-camel-case: true # 驼峰命名法映射 address_book ---> AddressBook
      ##    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志输出
      ##  mapper-locations: classpath:/mappers/**.xml # 指定xml位置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:/mappers/**.xml
  global-config:
    db-config:
      id-type: ASSIGN_ID # id生成策略类型

application.yaml

3.修改 当前的Mapper接口继承BaseMapper

八、IService

IService是MyBatis-Plus在service层提供的接口

当我们自定义的service层接口继承了IService接口之后;

开发人员自定义的接口就会继承拥有更多Iservice封装的通用逻辑方法;

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:6491b92d-8dc8-489d-a684-2e0f986c0767

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:c7d4e406-221f-4a0e-8b4c-ed2d270fad69

1.mapper层

mapper层定义的接口继承MyBatis-Plus提供的BaseMapper接口

@Mapper
@Repository
public interface TaskinfoLogsMapper extends BaseMapper {

}

2.service层接口

service层定义的接口,继承IService接口

public interface TaskinfoLogsService extends IService {
   }

3.service层实现类

service层实现类继承MyBatis-Plus提供的ServiceImpl基类,并实现以上在service层中定义的接口;

@Service
//Service实现类需要继承MyBatis-Plus通用的基类ServiceImpl
//基类ServiceImpl中包含2个泛型:第一个泛型 继承了BaseMapper的Mapper,第二个泛型对应的 Pojo
public class TaskinfoLogsServiceImpl extends ServiceImpl implements TaskinfoLogsService {
}

4.IService常用API

package com.leadnews.schedule;

import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.service.TaskinfoLogsService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class IServiceTest {
    @Autowired
    private TaskinfoLogsService  taskinfoLogsService;

    @Test
    public void test1(){
        //根据ID查询1条记录
        TaskinfoLogs taskinfoLogs = taskinfoLogsService.getById(1);
        System.out.println(taskinfoLogs);
    }
}

参考

Original: https://www.cnblogs.com/sss4/p/16325619.html
Author: Martin8866
Title: MyBatis-Plus

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/562595/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球