You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot/Spring测试中检查XML查询均含limit 1条件?

在Spring Boot测试中检查MyBatis XML查询是否包含limit 1

当然可以实现,这里提供两种实用方案,你可以根据场景选择:

方案一:直接扫描解析MyBatis XML文件

这种方法不需要执行SQL,直接读取XML文件做静态检查,适合快速验证所有查询配置。

实现步骤

  1. 在测试类中定位MyBatis XML文件的存放路径(通常是src/main/resources/mapper)
  2. 遍历所有.xml文件,解析每个文件里的<select>节点
  3. 提取查询的SQL内容,检查是否包含limit 1(可按需忽略大小写)
  4. 发现不符合要求的查询时,抛出断言异常终止测试

代码示例

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class MyBatisSqlStaticCheckTest {

    @Test
    void verifyAllSelectQueriesHaveLimit1() throws Exception {
        Path mapperRoot = Paths.get("src/main/resources/mapper");
        List<String> invalidQueries = new ArrayList<>();

        Files.walk(mapperRoot)
                .filter(path -> path.toString().endsWith(".xml"))
                .forEach(path -> {
                    try {
                        File xmlFile = path.toFile();
                        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                        Document doc = builder.parse(xmlFile);
                        doc.getDocumentElement().normalize();

                        NodeList selectNodes = doc.getElementsByTagName("select");
                        for (int i = 0; i < selectNodes.getLength(); i++) {
                            Element selectElem = (Element) selectNodes.item(i);
                            String queryId = selectElem.getAttribute("id");
                            String sql = selectElem.getTextContent().trim().toLowerCase();

                            if (!sql.contains("limit 1")) {
                                invalidQueries.add(String.format("文件[%s]中的查询[%s]未包含limit 1", xmlFile.getName(), queryId));
                            }
                        }
                    } catch (Exception e) {
                        throw new RuntimeException("解析XML失败: " + path, e);
                    }
                });

        assert invalidQueries.isEmpty() : "存在不符合要求的查询:\n" + String.join("\n", invalidQueries);
    }
}

方案二:用MyBatis拦截器在查询执行时检查

这种方法会在SQL实际执行前拦截检查,能覆盖动态生成的SQL(比如用<if>标签拼接的语句),适合集成测试场景。

实现步骤

  1. 自定义MyBatis拦截器,拦截StatementHandler的prepare方法
  2. 在拦截逻辑中获取待执行的SQL,检查是否包含limit 1
  3. 在测试类中注册拦截器,确保仅在测试环境生效
  4. 执行DAO层测试用例时,所有查询都会被校验,不符合则抛出异常

代码示例

自定义拦截器

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class SqlLimitCheckInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql().trim().toLowerCase();

        // 仅检查SELECT语句,可按需调整范围
        if (sql.startsWith("select")) {
            if (!sql.contains("limit 1")) {
                throw new AssertionError("查询SQL未包含limit 1: " + sql);
            }
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 可配置白名单等参数,比如忽略某些查询ID
    }
}

在测试类中注册拦截器

import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MyBatisInterceptorCheckTest {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @BeforeEach
    void setUp() {
        // 测试前注册拦截器
        sqlSessionFactory.getConfiguration().addInterceptor(new SqlLimitCheckInterceptor());
    }

    @Test
    void testFindById() {
        // 调用你的DAO方法,比如fooMapper.findById(1L);
        // 若SQL未包含limit 1,会直接抛出AssertionError
    }

    @Test
    void testFindByStatus() {
        // 调用对应的DAO方法
    }
}

补充说明

  • 如果有部分查询不需要检查limit 1,可以在逻辑中添加白名单(比如根据查询ID过滤)
  • 静态解析XML时要注意SQL的空格、换行和大小写问题,建议统一转小写后再校验
  • 拦截器方案能覆盖动态生成的SQL,静态解析方案仅能检查XML中写死的静态SQL

内容的提问来源于stack exchange,提问作者coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 13:26:31