MyBatis 拦截器

前言

MyBatis提供一种插件plugin的功能,虽是插件,但拥有拦截器的功能。

通过自定义拦截器可以完成字段自动填充,像创建、更新时间,删除标志,创建人等...
还可以完成自定义数据分页、数据权限的拼接替换等...

拦截范围

Executor(update,query,commit,rollback,close,isClosed) 拦截执行器的方法
ParamterHandler(getParamterObject setParamters) 拦截参数的处理
ResultSetHandler(handleResultSets,handleOutputparamters) 拦截结果集处理
StatementHandler(prepare,parameterize,batch,update,query) 拦截sql语法构建的处理

自定义拦截器

Executor

通用字段的自动填充

package com.datasource.config.mybatis;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 针对insert update操作对 创建人 创建时间 删除标志 更新人 更新时间 拦截填充
 *
 * @author Neoooo
 * @since 2023-08-28
 */
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class MyBatisOperateInterceptor implements Interceptor {

    private static final String CREATE_BY = "createBy";
    private static final String UPDATE_BY = "updateBy";
    private static final String CREATE_TIME = "createTime";
    private static final String UPDATE_TIME = "updateTime";
    private static final String IS_DELETE = "isDelete";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
        // 操作类型 只对 insert update 进行拦截
        SqlCommandType sqlCommandType = statement.getSqlCommandType();
        if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
            Object arg = invocation.getArgs()[1];
            if (arg instanceof Map<?, ?>) {
                for (Object obj : ((Map<?, ?>) arg).values()) {
                    insertOrUpdateOperate(obj, sqlCommandType);
                }
            } else {
                insertOrUpdateOperate(arg, sqlCommandType);
            }
        }
        return invocation.proceed();
    }


    /**
     * 添加或者
     *
     * @param object         数据对象
     * @param sqlCommandType 操作行为 insert or update
     */
    private void insertOrUpdateOperate(Object object, SqlCommandType sqlCommandType) throws IllegalAccessException {
        if (object == null) {
            log.info("object set properties ,object must is not null");
            return;
        }
        List<Field> declaredFields = new ArrayList<>(Arrays.asList(object.getClass().getDeclaredFields()));
        if (object.getClass().getSuperclass() != null && object.getClass().getSuperclass() != Object.class) {
            // 当前类具有超类父类(所有类都是继承于Object 所以要排除掉)
            Field[] superClassFields = object.getClass().getSuperclass().getDeclaredFields();
            declaredFields.addAll(Arrays.asList(superClassFields));
        }
        // 添加
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            if (SqlCommandType.INSERT.equals(sqlCommandType)) {
                System.out.println(declaredField.getName());
                switch (declaredField.getName()) {
                    case CREATE_BY:
                        // 创建人
                        declaredField.set(object, "Neoooo");
                        break;
                    case CREATE_TIME:
                        // 创建时间
                        declaredField.set(object, LocalDateTime.now());
                        break;
                    case IS_DELETE:
                        // 删除标志
                        declaredField.set(object, false);
                        break;
                    default:
                        break;
                }
            } else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
                switch (declaredField.getName()) {
                    case UPDATE_BY:
                        // 更新人 TODO 可获取当前登录用户
                        declaredField.set(object, "admin");
                        break;
                    case UPDATE_TIME:
                        // 更新时间
                        declaredField.set(object, LocalDateTime.now());
                        break;
                    default:
                        break;
                }
            }
        }
    }


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

    @Override
    public void setProperties(Properties properties) {

    }
}
/**
 * 针对myabtis拦截器的使用,注入至ioc容器即可
 *
 * @author Neoooo
 * @since 2023-08-28
 */
@Configuration
public class MyBatisConfiguration {
    @Bean
    public MyBatisOperateInterceptor myBatisOperateInterceptor() {
        return new MyBatisOperateInterceptor();
    }
}

StatementHandler

分页、数据权限的构建

package com.datasource.config.mybatis;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Properties;

/**
 * 针对sql做一些 封装
 * 分页
 * 权限拼接
 *
 * @author Neoooo
 * @since 2023-08-28
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MyBatisQueryInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 通过MetaObject优雅访问对象的属性,这里是访问statementHandler的属性;:MetaObject是Mybatis提供的一个用于方便、
        // 优雅访问对象属性的对象,通过它可以简化代码、不需要try/catch各种reflect异常,同时它支持对JavaBean、Collection、Map三种类型对象的操作。
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
        // 先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // id为执行的mapper方法的全路径名,如com.uv.dao.UserMapper.insertUser
        String id = mappedStatement.getId();
        // sql语句类型 select、delete、insert、update
        if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            // TODO 在此阶段可以做一些分页,数据权限的sql拼接,替换等处理
            sql = sql + " limit 1";
            Field sqlField = boundSql.getClass().getDeclaredField("sql");
            sqlField.setAccessible(true);
            sqlField.set(boundSql, sql);
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}

/**
 * 针对myabtis拦截器的使用,注入至ioc容器即可
 *
 * @author Neoooo
 * @since 2023-08-28
 */
@Configuration
public class MyBatisConfiguration {
    @Bean
    public MyBatisQueryInterceptor myBatisQueryInterceptor() {
        return new MyBatisQueryInterceptor ();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值