首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

基於 Digester 實現 XML 動態 SQL 參數配置:從原理到落地

     編輯:Java 綜合教學

基於 Digester 實現 XML 動態 SQL 參數配置:從原理到落地

在 SpringMVC + Spring + Hibernate 架構下,將 SQL 與 Java 代碼解耦、實現動態參數綁定,是提升維護性的關鍵。本文展示一套基於 Apache Commons Digester 3 的輕量級解決方案。

核心依賴

  • commons-digester3-3.2.jar:XML → JavaBean 映射
  • commons-beanutils-1.9.3.jar:屬性填充輔助

XML 規範設計

<?xml version="1.0" encoding="utf-8"?>
<set>
  <sql name="codeType">
    <pattern><![CDATA[
      select A.id, A.value, A.text
      from sys.code A
      left join sys.codeType B on A.codeTypeId = B.id
      where (case when "{0}"="" then 1=1 else B.codeType = "{0}" end)
        and (case when "{1}"="" then 1=1 else A.isEnable = "{1}" end)
        and (case when "{2}"="" then 1=1 else A.isDefault = "{2}" end)
    ]]></pattern>
    <param name="codeType" position="0" />
    <param name="isEnable" position="1" />
    <param name="isDefault" position="2" />
  </sql>
</set>

{0}、{1} 為 MessageFormat 佔位符,position 對應參數順序。

核心 JavaBean 映射

// SqlSet.java - 根節點
public class SqlSet {
    private Map<String, Sql> sqls = new HashMap<>();
    public void addSql(Sql sql) { sqls.put(sql.getName(), sql); }
    // getter/setter
}

// Sql.java - SQL 定義
public class Sql {
    private String name, pattern;
    private List<SqlParam> condition = new ArrayList<>();
    public void addParam(SqlParam p) { condition.add(p); }
    // getter/setter
}

// SqlParam.java - 參數元數據
public class SqlParam {
    private String name;
    private Integer position;
    // getter/setter
}

Digester 解析器實現

public class SqlXmlParser {
    private static final String WEB_ROOT = System.getProperty("webapp.root") + "sql/";
    private static final Digester DIGESTER = buildDigester();
    private final String sqlXml;

    private static Digester buildDigester() {
        Digester d = new Digester();
        d.setValidating(false);
        d.addObjectCreate("set", SqlSet.class);
        d.addObjectCreate("set/sql", Sql.class);
        d.addSetProperties("set/sql");           // 映射 name 屬性
        d.addBeanPropertySetter("set/sql/pattern");
        d.addSetNext("set/sql", "addSql");       // 關聯 SqlSet
        d.addObjectCreate("set/sql/param", SqlParam.class);
        d.addSetProperties("set/sql/param");     // 映射 name, position
        d.addSetNext("set/sql/param", "addParam");
        return d;
    }

    public String parse(String sqlName, Map<String, String> params) {
        try (InputStream is = new FileInputStream(WEB_ROOT + sqlXml)) {
            SqlSet set = (SqlSet) DIGESTER.parse(is);
            Sql sql = set.getSqls().get(sqlName);
            List<String> args = new ArrayList<>();
            for (SqlParam p : sql.getCondition()) {
                String v = params.get(p.getName());
                args.add(p.getPosition(), StringUtils.defaultString(v));
            }
            return MessageFormat.format(sql.getPattern(), args.toArray());
        } catch (Exception e) {
            log.error("SQL XML 解析失敗", e);
            return null;
        }
    }
}

Service 層調用示例

@Service("codeBS")
public class CodeBSImpl implements CodeBS {
    private static final String SQL_XML = "xxx/code.xml";
    @Autowired private CodeDao codeDao;

    @Override
    public List<Map<String, Object>> getValueTextListByType(String codeType) {
        Map<String, String> param = new HashMap<>();
        param.put("codeType", codeType);
        String sql = new SqlXmlParser(SQL_XML).parse("codeType", param);
        return codeDao.findBySql(sql).stream()
            .map(m -> Map.of("value", m.get("value"), "text", m.get("text")))
            .collect(Collectors.toList());
    }
}

優勢與擴展點

  • 零侵入:不依賴 MyBatis 等重型框架,適合輕量級改造。
  • 動態條件:case when 模式實現參數為空時自動忽略條件。
  • 擴展性:可增加 typeHandler、jdbcType 等屬性支援複雜類型轉換。

將 SQL 外部化,讓業務代碼回歸純粹,是架構演進的必經之路。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved