Mybatis 自定义参数拦截器

参考资料:

  1. 使用MyBatis拦截器后,摸鱼时间又长了。🐟


一. 🤔需求场景

如下图所示

  • 有一个订单信息表,每当插入或者更新一条数据的时候,红框所示部分的字段都需要进行赋值处理.
  • 如果还有其他表中也存在红框中的所示字段,每次插入或者更新时也都需要进行赋值操作,很是繁琐。
    在这里插入图片描述
    🐳有没有一种办法,能再插入或者更新数据库指定字段的时候,自动替我们处理呢?这就用到了Mybatis中的拦截器.

二. 🤠前期准备

⏹定义一个接口,用来标记实现了此接口的类中的参数会被自动处理

public interface AutoHandleParam {
}

⏹定义order_info表的映射实体类

import lombok.Data;
import java.util.Date;

@Data
public class OrderInfoEntity implements AutoHandleParam {

    private String orderId;

    private String orderName;

    private String createUserId;

    private Date createTime;

    private String updateUserId;

    private Date updateTime;
}

⏹查询接口

public interface OrderInfoMapper {

    void insertOrderInfo(OrderInfoEntity entity);

    void updateOrderInfo(OrderInfoEntity entity);
}

⏹反射封装类ReflectionUtil,详情见这篇文章 Java 封装反射工具类


三. 💪自定义拦截器

import com.example.jmw.common.utils.ReflectionUtil;
import com.example.jmw.entity.AutoHandleParam;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;

@Component
@Intercepts({
        @Signature(
            type = Executor.class, method = "update",
            args = {MappedStatement.class, Object.class}
        )
})
public class MybatisParamInterceptor implements Interceptor {
	
	// 需要拦截的Mybatis中的操作
    private final static String INSERT = "INSERT";
    private final static String UPDATE = "UPDATE";
    private final static List<String> sqlCommandTypeList = Arrays.asList(INSERT, UPDATE);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取Mybatis的当前操作方法名称
        String sqlCommandType = mappedStatement.getSqlCommandType().name();

        if (!sqlCommandTypeList.contains(sqlCommandType)) {
            return invocation.proceed();
        }

        if (invocation.getArgs().length < 2) {
            return invocation.proceed();
        }

        // 获取Mybatis插入或更新时传入的参数对象
        Object paramEntity = invocation.getArgs()[1];
        this.interceptInsertOrUpdateMethod(sqlCommandType, paramEntity);

        return invocation.proceed();
    }

    /**
     * 插入或更新时的参数拦截方法
     * @param methodName Mybatis方法名称
     * @param paramEntity 参数实体类
     */
    private void interceptInsertOrUpdateMethod(String methodName, Object paramEntity) {

        // 如果该对象没有实现AutoHandleParam接口的话,就不需要自动添加参数
        if (!(paramEntity instanceof AutoHandleParam)) {
            return;
        }

        // 系统默认的用户名
        String userId = "系统用户名" + System.currentTimeMillis();
        // 当前的系统时间
        Date systemDate = new Date();

        // 若Mybatis的当前方法为INSERT
        if (INSERT.equals(methodName)) {

            // 若实体类中存在此字段
            if (ReflectionUtil.existsField(paramEntity, "createUserId")) {
                Object createUserId = ReflectionUtil.invokeGetterMethod(paramEntity, "createUserId");
                if (ObjectUtils.isEmpty(createUserId) || "".equals(createUserId.toString())) {
                    // 通过反射将用户ID放入实体类中
                    ReflectionUtil.invokeSetterMethod(paramEntity, "createUserId", userId);
                }
            }

            if (ReflectionUtil.existsField(paramEntity, "createTime")) {
                Object createTime = ReflectionUtil.invokeGetterMethod(paramEntity, "createTime");
                if (ObjectUtils.isEmpty(createTime) ) {
                    ReflectionUtil.invokeSetterMethod(paramEntity, "createTime", systemDate);
                }
            }

            return;
        }

        // 若Mybatis的当前方法为UPDATE
        if (UPDATE.equals(methodName)) {

            if (ReflectionUtil.existsField(paramEntity, "updateUserId")) {
                ReflectionUtil.invokeSetterMethod(paramEntity, "updateUserId", userId);
            }

            if (ReflectionUtil.existsField(paramEntity, "updateTime")) {
                ReflectionUtil.invokeSetterMethod(paramEntity, "updateTime", systemDate);
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

四. 👉添加自定义拦截器到Mybatis中

  • 将自定义拦截器添加到Mybatis中,有两种方式
      1. 自定义一个方法,返回SqlSessionFactory到Spring的Bean中。
      1. 自定义一个方法,返回SqlSessionFactoryBeanCustomizer到Spring的Bean中。
  • 官方文件推荐将SqlSessionFactoryBeanCustomizer到Spring的Bean中,将SqlSessionFactory到Spring的Bean中的这种方式,会导致Mybatis的配置文件或配置类失效。因为无论配置文件还是配置类,最终都是加载到SqlSessionFactory中,如果我们自己手动new一个SqlSessionFactory到Bean中的话,相当于重置了Mybatis为我们自动配置好的SqlSessionFactory
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SqlSessionFactoryBeanCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    // 注入数据源
    @Resource
    private DataSource dataSource;

    // 注入自定义参数拦截器
    @Resource
    private MybatisParamInterceptor paramInterceptor;

    // @Bean  ===> ❗❗❗会造成Mybatis配置文件或者配置类失效,尽量不要这么写。
    public SqlSessionFactory sqlSessionFactorySecondary() throws Exception {

        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();

        // 设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置拦截器(可设置多个)
        Interceptor[] plugins = {paramInterceptor};
        sqlSessionFactoryBean.setPlugins(plugins);
        return sqlSessionFactoryBean.getObject();
    }

	// 💪💪💪官方推荐的实现方式
	@Bean
    SqlSessionFactoryBeanCustomizer sqlSessionFactoryBeanCustomizer() {
        return new SqlSessionFactoryBeanCustomizer() {
            @Override
            public void customize(SqlSessionFactoryBean customizeFactoryBean) {
                // 设置数据源
                customizeFactoryBean.setDataSource(dataSource);
                // 设置自定义拦截器(可设置多个)
                Interceptor[] plugins = {paramInterceptor};
                customizeFactoryBean.setPlugins(plugins);
            }
        };
    }
    
}

👉👉👉 官方文档截图
在这里插入图片描述


五. 😋效果

⭕操作之前,表中没有数据

在这里插入图片描述


⏹插入一条数据

  • 可以看到我们并没有向参数实体类中传入用户ID和创建时间,但是拦截器替我们向实体类中放入了参数,因此最终用户ID和创建时间插入到数据库中了。
    在这里插入图片描述

⏹更新一条数据

  • 可以看到指定的订单名称被更新,并且更新用户ID和更新时间也被自动处理到数据库中
    在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值