美文网首页
检查mybatis的配置文件是否包含某个字段

检查mybatis的配置文件是否包含某个字段

作者: 剑道_7ffc | 来源:发表于2020-03-28 15:57 被阅读0次

需求描述

在maven编译过程中,检查配置文件是否包含某个字段

设计思路

技术方案

maven自定义插件:解决maven在compile过程中执行
mybatis:自定义解析xml,来判断是否包含字段

变量

xml所在的文件夹,所检查的字段

核心代码

pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>my-plugin</groupId>
    <artifactId>my-maven-plugin</artifactId>
    <packaging>maven-plugin</packaging>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.3</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>
    <!--my:checkField -->
    <build>
        <plugins>
            <plugin>
                <groupId>my-plugin</groupId>
                <artifactId>my-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <configuration>
                    <mapperLocations>
                      <mapperLocation>${basedir}/src/main/resources</mapperLocation>
                    </mapperLocations>
                    <fieldName>parent_hospital_id</fieldName>
                    <filterElements>
                        <filterElement>Criteria_Clause</filterElement>
                    </filterElements>
                    <filterNames>
                        <filterName>a.xml</filterName>
                    </filterNames>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
java代码
public class SqlContext {
    private String sqlText;//sql内容
    private String fragmentName;//片段名称
    private String fragmentQualifiedName;//片段全限定名称
    private String fileName;//文件名
    private boolean filter = false;//是否过滤
}
/**
 * xml的where条件是否包含某个字段
 */
@Mojo(name = "checkField", defaultPhase = LifecyclePhase.COMPILE)
public class XmlWhereFieldCheck extends AbstractMojo {
    @Parameter
    private List<String> mapperLocations = new ArrayList<>();
    @Parameter
    private String fieldName;
    @Parameter
    private List<String> filterElements = new ArrayList<>();
    @Parameter
    private List<String> filterNames = new ArrayList<>();

    //命令空间+id --> sql
    Map<String, SqlContext> queryUpdateDeleteSqlMap = Maps.newHashMap();
    //命令空间+id --> sql片段
    Map<String, SqlContext> sqlNodeFragmentMap = Maps.newHashMap();
    //命令空间+id --> sql片段
    Map<String, SqlContext> queryUpdateDeleteNodeFragmentMap = Maps.newHashMap();
    // 创建saxReader对象
    SAXReader reader = new SAXReader();

    {
        try {
            //不解析DOCTYPE,防止破坏标准的解析文件
            reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",   false);
        } catch (SAXException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void execute() {
        System.out.println("**************参数***************");
        for (String mapperLocation : mapperLocations) {
            System.out.println("路径:" + mapperLocation);
        }
        for (String filterElement : filterElements) {
            System.out.println("片段名:" + filterElement);
        }

        for (String filterName : filterNames) {
            System.out.println("文件名:" + filterName);
        }

        System.out.println("匹配字段:" + fieldName);

        //解析文件
        System.out.println("**************解析文件***************");
        this.parseFiles();

        //解析片段
        System.out.println("********************解析片段********************");
        this.parseFragment();

        //获取未匹配的ids
        System.out.println("********************字段匹配********************");
        List<SqlContext> notMatcIds = this.notMatchField();

        //按照名称正序排序
        System.out.println("************************************");
        for (SqlContext sqlContext : notMatcIds) {
            System.out.println("未匹配的文件名: " + sqlContext.getFileName() + ",片段名:" + sqlContext.getFragmentName());
        }
        System.out.println("************************************");
    }

    /**
     * 未匹配的字段
     */
    private List<SqlContext> notMatchField(){
        String regex = "where.*" + fieldName + "(=|in)";
        Pattern pattern = Pattern.compile(regex);

        List<SqlContext> notMatchIds = new ArrayList<>();
        for (String key : queryUpdateDeleteSqlMap.keySet()) {
            SqlContext sqlContext = queryUpdateDeleteSqlMap.get(key);

            String value = sqlContext.getSqlText().toLowerCase();

           if(!pattern.matcher(value).find()){
               notMatchIds.add(sqlContext);
           }
        }
        return notMatchIds;
    }

    /**
     * 解析文件
     */
    private void parseFiles(){
        for (String location : mapperLocations) {
            File file = new File(location);
            if (file.isFile()) {
                this.parseFile(file);
            } else {
                this.scannerFiles(location);
            }
        }
    }

    /**
     * 扫描所有的文件
     */
    private void scannerFiles(String location){
        //替换多个/或\为1个
        location = location.replaceAll("(/+|\\+)", Matcher.quoteReplacement(File.separator));
        System.out.println("文件位置: " + location);

        File file = new File(location);
        if(!file.exists()){
            System.out.println("文件不存在:" + location);
            return;
        }

        File[] files = file.listFiles();
        for (File fileItem : files) {
            String filePath = location + File.separator + fileItem.getName();
            if(fileItem.isDirectory()){
                this.scannerFiles(filePath);
            }else {
                this.parseFile(fileItem);
            }
        }
    }

    /**
     * 解析文件
     */
    private void parseFile(File file) {
        try{
            // 通过read方法读取一个文件 转换成Document对象
            Document document = reader.read(file);
            System.out.println("文件名:" + file.getName());

            // 获取根节点元素对象
            Element node = document.getRootElement();
            String namespace = node.attribute("namespace").getValue();

            for (Object elementItem : node.elements()) {
                if(!(elementItem instanceof DefaultElement)){
                    continue;
                }

                Element element = (DefaultElement) elementItem;
                if("resultMap".equalsIgnoreCase(element.getName()) || "insert".equalsIgnoreCase(element.getName())){
                    continue;
                }

                String idName = element.attributeValue("id");
                String sqlKey = namespace + "." + idName;
                String sqlText = this.getAllText(namespace, element).replaceAll("\\s", "");

                SqlContext sqlContext = new SqlContext()
                        .setSqlText(sqlText)
                        .setFileName(file.getName())
                        .setFragmentName(idName)
                        .setFragmentQualifiedName(sqlKey)
                        ;
                sqlContext.setFilter(this.filter(sqlContext));//是否过滤
                if("sql".equalsIgnoreCase(element.getName())){
                    sqlNodeFragmentMap.put(sqlKey,sqlContext);
                }else{
                    queryUpdateDeleteNodeFragmentMap.put(sqlKey, sqlContext);
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 是否过滤
     */
    private boolean filter(SqlContext sqlContext){
        //若名字包含,则过滤
        if(null != filterNames && filterNames.contains(sqlContext.getFileName())){
            return true;
        }

        //若片段包含,则过滤
        if(null != filterElements && filterElements.contains(sqlContext.getFragmentName())){
            return true;
        }
        return false;
    }

    /**
     * 解析片段
     */
    private void parseFragment(){
        Pattern pattern = Pattern.compile("¥\\{[A-Za-z0-9\\._]+\\}");
        for (String key : queryUpdateDeleteNodeFragmentMap.keySet()) {
            SqlContext sqlContext = queryUpdateDeleteNodeFragmentMap.get(key);

            //过滤
            if(sqlContext.isFilter()){
                continue;
            }

            String sqlFragment = sqlContext.getSqlText();//sql片段

            Matcher matcher = pattern.matcher(sqlFragment);
            boolean filter = false;//是否过滤
            while(matcher.find()){
                String attributeName = matcher.group();
                attributeName = attributeName.substring(2, attributeName.length() - 1);

                //替换为<sql></sql>的sql片段
                String sqlNodeFragment = null;
                if(sqlNodeFragmentMap.containsKey(attributeName)){
                    SqlContext sqlNodeContext = sqlNodeFragmentMap.get(attributeName);
                    if(sqlNodeContext.isFilter()){
                        filter = true;
                        break;
                    }

                    sqlNodeFragment = Matcher.quoteReplacement(sqlNodeContext.getSqlText());
                }else{
                    System.out.println("没有找到片段:" + attributeName);

                    sqlNodeFragment = "";
                }
                sqlFragment = sqlFragment.replaceAll("¥\\{" + attributeName + "\\}", sqlNodeFragment);

                matcher = pattern.matcher(sqlFragment);
            }
            if(!filter){//没有过滤,则添加
                queryUpdateDeleteSqlMap.put(key, sqlContext.setSqlText(sqlFragment));
            }
        }
    }

    /**
     * 获取结点和子节点拼接的结点内容
     */
    public String getAllText(String namespace, Element element){
        StringBuilder sb = new StringBuilder();

        String nodeName = element.getName().toLowerCase();
        if("include".equalsIgnoreCase(nodeName)){
            String refidValue = element.attributeValue("refid");
            //若包含.则使用使用refidValue,否则使用namespace + refidValue的值
            String sqlKey = Pattern.compile("\\.+").matcher(refidValue).find() ? refidValue : (namespace + "." + refidValue);
            return sb.append("¥{")
                    .append(sqlKey)
                    .append("}").toString();
        }
        if("trim".equalsIgnoreCase(nodeName)){
            sb.append(element.attributeValue("prefix", ""));
        }
        if("where".equalsIgnoreCase(nodeName)){
            sb.append("where");
        }

        for (Object elementItem : element.content()) {
            if (elementItem instanceof FlyweightText) {//文本
                sb.append(((FlyweightText) elementItem).getText());
            } else if (elementItem instanceof DefaultElement) {//元素
                sb.append(getAllText(namespace, (DefaultElement) elementItem));
            } else if(elementItem instanceof FlyweightCDATA){//
                sb.append(((FlyweightCDATA) elementItem).getText());
            } else if(elementItem instanceof FlyweightComment) {//注释,不增加
            } else {
                throw new RuntimeException("不合法的元素类型");
            }
        }

        return sb.toString();
    }

    public static void main(String[] args) {
        XmlWhereFieldCheck xmlWhereFieldCheck = new XmlWhereFieldCheck();
        File file = new File("src\\main\\resources\\OperateLog_sql.xml");
        xmlWhereFieldCheck.parseFile(file);
        xmlWhereFieldCheck.parseFragment();
        List<SqlContext> notMatcIds = xmlWhereFieldCheck.notMatchField();

        for (String key : xmlWhereFieldCheck.queryUpdateDeleteSqlMap.keySet()) {
            String value = xmlWhereFieldCheck.queryUpdateDeleteSqlMap.get(key).getSqlText();
            String regex = "(where|WHERE).*group_hospital_id(=|in|IN)";
            Pattern pattern = Pattern.compile(regex);
            System.out.println(pattern.matcher(value).find());

            System.out.println(key + "  === " + value);
        }
    }
}

执行

自动执行

加executions

    <build>
        <plugins>
            <plugin>
                <groupId>my-plugin</groupId>
                <artifactId>my-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <!--在执行package时候自动执行自定义插件 将插件挂到 phase 中 -->
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>checkField</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
手动执行

my:checkField

相关文章

网友评论

      本文标题:检查mybatis的配置文件是否包含某个字段

      本文链接:https://www.haomeiwen.com/subject/juqsuhtx.html