Mybatis拦截器统一管理创建人、更新人、创建时间、更新时间等公共字段

  • Post author:
  • Post category:其他


之前写过


MybatisPlus 统一管理创建人、更新人、创建时间、更新时间等公共字段_明湖起风了的博客-CSDN博客

但是如果我们实际开发中用的不是 MybatisPlus 而是 Mybatis 怎么实现呐?这就用到了mybatis拦截器。

Mybatis拦截器是什么?

MyBatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用,通过织入拦截器,在不同节点修改一些执行过程中的关键属性,从而影响SQL的生成、执行和返回结果,如:来影响Mapper.xml到SQL语句的生成、执行SQL前对预编译的SQL执行参数的修改、SQL执行后返回结果到Mapper接口方法返参POJO对象的类型转换和封装等。

根据上面的对Mybatis拦截器作用的描述,可以分析其可能的用途;最常见的就是Mybatis自带的分页插件PageHelper或Rowbound参数,通过打印实际执行的SQL语句,发现我们的

分页查询之前





执行了COUNT(*)语句查询数量,

然后再执行

查询时修改了SQL语句即在我们写的SQL语句后拼接上了分页语句LIMIT(offset, pageSize);

此外,实际工作中,可以使用Mybatis拦截器来做一些数据过滤、数据加密脱敏、SQL执行时间性能监控和告警等;

这里读者自行了解


MybatisPlus 统一管理创建人、更新人、创建时间、更新时间等公共字段_明湖起风了的博客-CSDN博客

实现:

pom

<?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>com.example</groupId>
    <artifactId>mybatisInterceptor</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatisInterceptor</name>
    <description>mybatisInterceptor</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-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Mybatis核心 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.example.mybatisinterceptor.MybatisInterceptorApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

1. 抽取公共字段创建父类基础模型

package com.example.mybatisinterceptor.bean;

import com.example.mybatisinterceptor.MyInterface.CreateBy;
import com.example.mybatisinterceptor.MyInterface.CreateTime;
import com.example.mybatisinterceptor.MyInterface.UpdateBy;
import com.example.mybatisinterceptor.MyInterface.UpdateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

/**
 * 数据库模型设计时抽出所有通用字段,抽象为父类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class BaseEntity implements Serializable {

    @CreateTime
    //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    @UpdateTime
    //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date updateTime;

    @CreateBy
    private String creator;

    @UpdateBy
    private String updater;

    private Boolean deleted;

}

2. java中所有业务实体继承该父类基础模型

package com.example.mybatisinterceptor.bean;

import lombok.*;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyTableEntity extends BaseEntity {

    /**
     * 用户ID
     */
    private Long id;
    /**
     * 用户账号
     */
    private String userName;
    /**
     * 加密后的密码
     */
    private String passWord;

}

3. mapper

package com.example.mybatisinterceptor.mapper;

import com.example.mybatisinterceptor.bean.MyTableEntity;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface MyTableMapper {
    public int insert(MyTableEntity myTableEntity);
    public int update(MyTableEntity myTableEntity);
}


mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.mybatisinterceptor.mapper.MyTableMapper">
    <insert id="insert" parameterType="com.example.mybatisinterceptor.bean.MyTableEntity" useGeneratedKeys="true" keyProperty="id">
        insert into im_base_entity (username,password,create_time,update_time,creator,updater,deleted)
            values (#{userName},#{passWord},#{createTime},#{updateTime},#{creator},#{updater},0);
    </insert>
    <update id="update">
        update im_base_entity
        <set>
            <if test="userName!=null">
                username=#{userName},
            </if>

            <if test="passWord!=null">
                password=#{passWord},
            </if>

            <if test="createTime!=null">
                create_time=#{createTime},
            </if>

            <if test="updateTime!=null">
                update_time=#{updateTime},
            </if>

            <if test="creator!=null">
                creator=#{creator},
            </if>

            <if test="updater!=null">
                updater=#{updater}
            </if>
        </set>
        where id=#{id}
    </update>
</mapper>

4.  MyTableService

package com.example.mybatisinterceptor.service;

import com.example.mybatisinterceptor.bean.MyTableEntity;
import org.springframework.transaction.annotation.Transactional;

@Transactional(rollbackFor = Exception.class)
public interface MyTableService {
    public int insert(MyTableEntity myTableEntity);
    public int update(MyTableEntity myTableEntity);
}


MyTableServiceImpl

package com.example.mybatisinterceptor.service;

import com.example.mybatisinterceptor.bean.MyTableEntity;
import com.example.mybatisinterceptor.mapper.MyTableMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyTableServiceImpl implements MyTableService{

    @Autowired
    private MyTableMapper mapper;

    @Override
    public int insert(MyTableEntity myTableEntity) {
        return mapper.insert(myTableEntity);
    }

    @Override
    public int update(MyTableEntity myTableEntity) {
        return mapper.update(myTableEntity);
    }
}

5. controller

package com.example.mybatisinterceptor.controller;

import com.example.mybatisinterceptor.bean.MyTableEntity;
import com.example.mybatisinterceptor.mapper.MyTableMapper;
import com.example.mybatisinterceptor.service.MyTableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
public class baseController {

    @Autowired
    private MyTableService myTableService;

    @PostMapping("/insert")
    public int insert (@RequestBody MyTableEntity myTableEntity){
        System.out.println(myTableEntity);
        return myTableService.insert(myTableEntity);
    }

    @PostMapping("/uptate")
    public int uptate (@RequestBody MyTableEntity myTableEntity){
        System.out.println(myTableEntity);
        return myTableService.update(myTableEntity);
    }
}


别忘了 yml 与mapper扫描

server:
  port: 8088
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sg_security?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  mapper-locations: classpath:/mapper/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true


6. 核心 自定义注解

package com.example.mybatisinterceptor.MyInterface;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自动设置创建人
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CreateBy {
    String value() default "";
}

package com.example.mybatisinterceptor.MyInterface;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自动设置创建时间
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CreateTime {
    String value() default "";
}
package com.example.mybatisinterceptor.MyInterface;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自动设置修改人
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface UpdateBy {
    String value() default "";
}
package com.example.mybatisinterceptor.MyInterface;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自动设置修改时间
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface UpdateTime {
    String value() default "";
}

7. 核心 自定义过滤器

package com.example.mybatisinterceptor.interceptor;

import com.example.mybatisinterceptor.MyInterface.CreateBy;
import com.example.mybatisinterceptor.MyInterface.CreateTime;
import com.example.mybatisinterceptor.MyInterface.UpdateBy;
import com.example.mybatisinterceptor.MyInterface.UpdateTime;
import com.example.mybatisinterceptor.bean.BaseEntity;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;

/**
 * 自定义 Mybatis 插件,自动设置 createTime 和 updatTime 的值。
 * 拦截 update 操作(添加和修改)
 */
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class CustomInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(CustomInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];

        // 获取 SQL 命令
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        //只判断新增和修改
        if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
            // 获取参数
            Object parameter = invocation.getArgs()[1];
            //批量操作时
            if (parameter instanceof MapperMethod.ParamMap) {
                MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameter;
                Object obj = map.get("list");
                List<?> list = (List<?>) obj;
                if (list != null) {
                    for (Object o : list) {
                        setParameter(o, sqlCommandType);
                    }
                }
            } else {
                setParameter(parameter, sqlCommandType);
            }
        }
        return invocation.proceed();
    }

    public void setParameter(Object parameter, SqlCommandType sqlCommandType) throws Throwable {
        Class<?> aClass = parameter.getClass();
        Field[] declaredFields;
        //如果常用字段提取了公共类 BaseEntity
        //判断BaseEntity是否是父类
        if (BaseEntity.class.isAssignableFrom(aClass)) {
            // 获取父类私有成员变量
            declaredFields = aClass.getSuperclass().getDeclaredFields();
        } else {
            // 获取私有成员变量
            declaredFields = aClass.getDeclaredFields();
        }
        for (Field field : declaredFields) {
            if (SqlCommandType.INSERT.equals(sqlCommandType)) { // insert 语句插入 createBy
                if (field.getAnnotation(CreateBy.class) != null) {
                    field.setAccessible(true);
                    // 这里实际开发中获取登录用户名,填写真实值
                    field.set(parameter, "fan");
                }

                if (field.getAnnotation(CreateTime.class) != null) { // insert 语句插入 createTime
                    field.setAccessible(true);
                    field.set(parameter, new Date());
                }
            }

            if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
                if (field.getAnnotation(UpdateTime.class) != null) { // update 语句插入 updateTime
                    field.setAccessible(true);
                    field.set(parameter, new Date());
                }
                if (field.getAnnotation(UpdateBy.class) != null) { // update 语句插入 updateBy
                    field.setAccessible(true);
                    // 这里实际开发中获取登录用户名,填写真实值
                    field.set(parameter, "fan");
                }
            }
        }
    }
}


测试:添加


结果


更新


结果

至此完毕



版权声明:本文为m0_47743175原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。