摘要

本文通过代码展示如何自定义一个mybatis拦截器以实现SQL语句的再次封装。

拦截目的

为了在特定业务场景下实现SQL语句的全局封装,而无需在多个逻辑类中手动调用转换方法。这里为了实现输入%或者_时能够查找到带%或_字符的数据内容,一般来说%和_是数据库的特殊字符,是一种通配符,会把所有内容模糊查找出来。

示例代码

1)导入依赖pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!-- mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!-- sql性能分析插件 -->
    <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
    </dependency>
</dependencies>

2)自定义配置application.yml

#这里省略其他mybatis的默认配置
spring:
datasource:
    type:com.alibaba.druid.pool.DruidDataSource
    driverClassName:com.p6spy.engine.spy.P6SpyDriver #使用P6Spy驱动
    url:jdbc:p6spy:mysql://192.168.233.129:3306/CoffeeBeansDB?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
    username:xxx
    password:xxx

mybatis:
custom-search:
    fields:search,input # 自定义拦截指定的搜索字段

3)读取动态拦截参数配置

package org.coffeebeans.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;

/**
 * <li>ClassName: SearchParamsConfig </li>
 * <li>Author: OakWang </li>
 */
@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "mybatis.custom-search")
public class SearchParamsConfig {
    private List<String> fields;
}

4)自定义拦截类

package org.coffeebeans.config;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;

/**
 * <li>ClassName: QueryInterceptor </li>
 * <li>Author: OakWang </li>
 */
@Component
@Intercepts({
       @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryInterceptor implements Interceptor {

    @Autowired
    private SearchParamsConfig searchParamsConfig;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
       Object[] args = invocation.getArgs();
       Object parameter = args[1];

       if (searchParamsConfig.getFields() == null || searchParamsConfig.getFields().isEmpty()) {
          return invocation.proceed(); // 如果没有配置字段,则直接返回
       }

       if (parameter instanceof Map) {
          handleMapParameter((Map<String, Object>) parameter);
       } else {
          handlePojoParameter(parameter);
       }

       return invocation.proceed();
    }

    private void handleMapParameter(Map<String, Object> paramMap) {
       for (String fieldName : searchParamsConfig.getFields()) {
          if (paramMap.containsKey(fieldName)) {
             String value = (String) paramMap.get(fieldName);
             if (value != null) {
                paramMap.put(fieldName, "%" + value.replace("%""%").replace("_""_") + "%");
             }
          }
       }
       //TODO 扩展 转义 % 和 _(根据数据库类型调整)
       //    if (isMySQL()) { 
       //       //
       //    } else if (isSQLServer()) {
       //       //
       //    } else if (isPostgreSQL()) {
       //       //
       //    }
    }

    private void handlePojoParameter(Object pojo) {
       for (String fieldName : searchParamsConfig.getFields()) {
          try {
             Field field = pojo.getClass().getDeclaredField(fieldName);
             field.setAccessible(true);  //实现不区分大小写的字段匹配(需自行处理大小写转换)
             Object value = field.get(pojo);
             if (value instanceof String) {
                field.set(pojo, "%" + ((String)value).replace("%""%").replace("_""_") + "%");
             }
          } catch (NoSuchFieldException | IllegalAccessException ignored) {
             // 如果字段不存在或不可访问,则跳过
          }
       }
    }

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

    @Override
    public void setProperties(Properties properties) {
       // 不需要额外处理
    }
}

5)定义ORM

实体

package org.coffeebeans.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

/**
 * <li>ClassName: User </li>
 * <li>Author: OakWang </li>
 */
@Data
public class User {
    @TableField("id")
    private Long id;

    @TableField("name")
    private String name;

    @TableField("create_time")
    private String createTime;

}

mapper

package org.coffeebeans.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.coffeebeans.entity.User;
import java.util.List;

@Mapper
public interface UserMapper {
    List<User> selectBySearch(@Param("search") String search);
}

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="org.coffeebeans.mapper.UserMapper">

    <select id="selectBySearch" resultType="org.coffeebeans.entity.User">
        SELECT * FROM User WHERE name LIKE #{search}
    </select>

</mapper>

6)测试类

package org.coffeebeans;

import lombok.extern.slf4j.Slf4j;
import org.coffeebeans.service.InterceptorService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * <li>ClassName: org.org.coffeebeans.InterceptorTest </li>
 * <li>Author: OakWang </li>
 */
@Slf4j
@SpringBootTest
public class InterceptorTest {


    @Autowired
    private InterceptorService interceptorService;

    @Test
    void test1() {
        interceptorService.selectBySearch("1%");
    }

    @Test
    void test2() {
        interceptorService.selectBySearch("张_");
    }

}

7)测试拦截情况

建测试数据

只找“1%”的数据,而不是所有带“1”的数据

只找“张_”的数据,而不是以“张”开头的数据

图片

断点查看拦截情况

图片

打印SQL,查看拦截后的语句

图片图片

测试配置修改

图片

总结

以上我们了解了如何在springboot项目中自定义mybatis拦截器以实现sql的全局封装。

关注公众号:咖啡Beans

在这里,我们专注于软件技术的交流与成长,分享开发心得与笔记,涵盖编程、AI、资讯、面试等多个领域。无论是前沿科技的探索,还是实用技巧的总结,我们都致力于为大家呈现有价值的内容。期待与你共同进步,开启技术之旅。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]