解决SpringMVC使用MyBatis-Plus自定义MyBaits拦截器不生效的问题

自定义MyBatis拦截器

如果是SpringBoot项目引入@Component注解就生效了,但是SpringMVC不行

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
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.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class,}),
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
})
//@Component 在SpringBoot生效,在SpringMVC不生效
public final class MyBatisSqlParsingPlugin implements Interceptor {
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        ParameterHandler parameterHandler = statementHandler.getParameterHandler();
        BoundSql boundSql = statementHandler.getBoundSql();
        try {
            String sql = formatSql(parameterHandler, boundSql);
            if (!boundSql.getSql().equals(sql)) {
                log.info("Execute SQL:{}", sql);
            }
        } catch (Exception e) {
            String sql = boundSql.getSql();
            log.error("Execute SQL:{}\nException:", sql, e);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }

    /**
     * 格式化SQL及其参数
     */
    private String formatSql(ParameterHandler parameterHandler, BoundSql boundSql) throws NoSuchFieldException, IllegalAccessException {

        Class<? extends ParameterHandler> parameterHandlerClass = parameterHandler.getClass();
        Field mappedStatementField = parameterHandlerClass.getDeclaredField("mappedStatement");
        mappedStatementField.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) mappedStatementField.get(parameterHandler);

        String sql = boundSql.getSql().replaceAll("\\s+", " ");

        // sql字符串是空或存储过程,直接跳过
        if (!StringUtils.hasText(sql) || sql.trim().charAt(0) == '{') {
            return "";
        }

        // 不传参数的场景,直接把Sql美化一下返回出去
        Object parameterObject = parameterHandler.getParameterObject();
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
        if (Objects.isNull(parameterObject) || parameterMappingList.isEmpty()) {
            return sql;
        }

        return handleCommonParameter(sql, boundSql, mappedStatement);
    }

    //替换预编译SQL
    private String handleCommonParameter(String sql, BoundSql boundSql, MappedStatement mappedStatement) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Configuration configuration = mappedStatement.getConfiguration();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        List<String> params = new ArrayList<>();

        for (ParameterMapping parameterMapping : parameterMappings) {
            if (parameterMapping.getMode() == ParameterMode.OUT) {
                continue;
            }
            Object propertyValue;
            String propertyName = parameterMapping.getProperty();
            if (boundSql.hasAdditionalParameter(propertyName)) {
                propertyValue = boundSql.getAdditionalParameter(propertyName);
            } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                propertyValue = parameterObject;
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                propertyValue = metaObject.getValue(propertyName);
            }
            params.add(this.formatParam(propertyValue));
        }

        //转译百分号
        if (sql.contains("%")) {
            //如果参数不一致直接返回SQL
            Pattern pattern = Pattern.compile("\\?");
            Matcher matcher = pattern.matcher(sql);
            int count = 0;
            while (matcher.find()) {
                count++;
            }
            if (count == 0 || params.isEmpty()) {
                return sql;
            }
            if (params.size() != count) {
                log.error("SQL:{}", sql);
                log.error("SQL parameters:{}", params);
                return sql;
            }
            sql = sql.replaceAll("%", "%%");
        }

        sql = sql.replaceAll("\\?", "%s");
        return String.format(sql, params.toArray());
    }

    private String formatParam(Object object) {
        if (object == null) {
            return "null";
        }
        if (object instanceof String) {
            return formatString((String) object);
        }
        if (object instanceof Date) {
            return formatDate((Date) object);
        }
        if (object instanceof LocalDate) {
            return formatLocalDate((LocalDate) object);
        }
        if (object instanceof LocalDateTime) {
            return formatLocalDateTime((LocalDateTime) object);
        }
        return object.toString();
    }

    private static String formatString(String str) {
        return "'" + str + "'";
    }

    private String formatDate(Date date) {
        return "'" + DATE_TIME_FORMATTER.format(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()) + "'";
    }

    private String formatLocalDate(LocalDate date) {
        return "'" + DATE_FORMATTER.format(date) + "'";
    }

    private String formatLocalDateTime(LocalDateTime dateTime) {
        return "'" + DATE_TIME_FORMATTER.format(dateTime) + "'";
    }
}

mybatis-plus使用mybatis插件

MybatisPlusInterceptor实现的是MyBaits Interceptor接口,而MybatisPlusInterceptor自己又创建了一个InnerInterceptor接口去实现自己的插件,比如:PaginationInnerInterceptor 分页插件。我们使用的是MyBatis Interceptor的插件,所以不能注入到MybatisPlusInterceptor类里面,而是要和MybatisPlusInterceptor平级,因为前面也说了MybatisPlusInterceptor 实现的是MyBaits Interceptor,而我们自定义的MyBatisSqlParsingPlugin实现的也是MyBaits Interceptor,因此要和MybatisPlusInterceptor平级放到plugins下面,建议自定义的MyBaits Interceptor要放到MybatisPlusInterceptor前面,防止MybatisPlusInterceptor干扰我们自定义的插件。

	<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath*:mapper/**/*.xml"/>
        <property name="typeAliasesPackage" value="com.bsoft.extend.mybatis.entity"/>
        <property name="plugins">
            <array>
            	<!-- 引入的是 MyBatis 插件 -->
                <ref bean="myBatisSqlParsingPlugin"/>
            	<!-- 引入的是 MyBatis-Plus 自己封装的插件 -->
                <ref bean="mybatisPlusInterceptor"/>
            </array>
        </property>
    </bean>
	
	<!-- 实现 MyBatis Interceptor 接口的插件 -->
	<bean id="myBatisSqlParsingPlugin" class="com.bsoft.extend.mybatis.plugins.MyBatisSqlParsingPlugin"/>

    <!-- 实现 MyBatis-Plus InnerInterceptor 接口的插件【就是MyBatis-Plus自己封装的插件】 -->
    <bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
        <property name="interceptors">
            <list>
            	<!-- 引入 MyBatis-Plus 分页插件 -->
                <ref bean="paginationInnerInterceptor"/>
            </list>
        </property>
    </bean>
    
    <!-- MyBatis-Plus 分页插件配置 -->
    <bean id="paginationInnerInterceptor" class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor">
        <constructor-arg name="dbType" value="ORACLE"/>
        <property name="optimizeJoin" value="true"/>
    </bean>
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 首先,需要在pom.xml文件中添加mybatis-plus的依赖: ``` <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.4.3.1</version> </dependency> ``` 2. 在Spring MVC的配置文件中,添加mybatis-plus的配置: ``` <!-- Mybatis Plus配置 --> <bean id="mybatisPlusConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig"> <property name="dbConfig" ref="dbConfig"/> <property name="sqlInjector" ref="logicSqlInjector"/> <property name="metaObjectHandler" ref="metaObjectHandler"/> </bean> <!-- 数据库配置 --> <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig"> <property name="logicDeleteValue" value="1"/> <property name="logicNotDeleteValue" value="0"/> <property name="idType" value="AUTO"/> </bean> <!-- 注入自定义的元对象处理 --> <bean id="metaObjectHandler" class="com.example.MyMetaObjectHandler"/> <!-- 注入逻辑删除插件 --> <bean id="logicSqlInjector" class="com.baomidou.mybatisplus.extension.injector.LogicSqlInjector"> <property name="logicDeleteSqlInjector" ref="myLogicDelete"/> </bean> <!-- 注入自定义的逻辑删除解析 --> <bean id="myLogicDelete" class="com.example.MyLogicDelete"/> ``` 3. 在Spring MVC的配置文件中,添加mybatis-plus的mapper扫描: ``` <!-- Mybatis Plus Mapper扫描 --> <bean class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath*:mapper/*.xml"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="globalConfig" ref="mybatisPlusConfig"/> </bean> <bean class="com.baomidou.mybatisplus.extension.spring.MybatisMapperScannerConfigurer"> <property name="basePackage" value="com.example.mapper"/> </bean> ``` 4. 在mapper接口中,使用mybatis-plus提供的注解,例如: ``` @Repository public interface UserMapper extends BaseMapper<User> { @Select("SELECT * FROM user WHERE username = #{username}") User findByUsername(@Param("username") String username); } ``` 5. 在service中,调用mapper接口中的方法,例如: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User findByUsername(String username) { return userMapper.findByUsername(username); } } ``` 这样,就完成了Spring MVC和mybatis-plus的整合配置。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值