Spring 项目中把属性或 SQL 语句写在 .xml 文件中

Spring 项目中把大量的 SQL 分散在 Java 代码中,无 Here Doc 的情况下用加号来连接写着实在是不爽,于是之前思考这个 Spring 项目中把 SQL 语句写在 .sql 文件中 --  把它们写在 *.sql 文件中,但是这个 *.sql 需要特定的格式来标识属性 Key

--!select.user
select id, firstname, lastname, address

--!update.user
update ........

而且还需要一个额外的类 SqlPropertySourceFactory 来解析上面的 *.sql 文件, 识别出 select.user 是 Key, 紧接着后面的块是相应的属性值,用注解引用它时还有点额外的 factory 属性来配置,如

@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)

所以一直在思考是否能够再简单些,是否能用一个自定义的注解,如

@SqlPropertySource("classpath:sql/queries.sql")

捉摸了很久,似乎有点难度,不过再不断发掘的过程中找到了这个类 org.springframework.core.io.support.PropertiesLoaderUtils, 有下面的代码片断

public static Properties loadProperties(EncodedResource resource) throws IOException {
    Properties props = new Properties();
    fillProperties(props, resource);
    return props;
}
......
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
   throws IOException {

    InputStream stream = null;
    Reader reader = null;
    try {
        String filename = resource.getResource().getFilename();
        if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
            stream = resource.getInputStream();
            persister.loadFromXml(props, stream);
        }
        else if (resource.requiresReader()) {
......

也就是说其实我们是可以用 XML 文件来定义属性的。

继续深挖到 run.util.spi.XmlPropertiesProvider, 这居然是来自于 rt.jar, 原来用 XML 定义属性由来已久。Spring 所用的一个实现同样是 rt.jar 中的 sun.util.xml.PlatformXmlPropertiesProvider, 看到这个类的前几行我瞬间就豁然开朗了

private static final String PROPS_DTD_URI = "http://java.sun.com/dtd/properties.dtd";
private static final String PROPS_DTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!-- DTD for properties --><!ELEMENT properties ( comment?, entry* ) ><!ATTLIST properties version CDATA #FIXED \"1.0\"><!ELEMENT comment (#PCDATA) ><!ELEMENT entry (#PCDATA) ><!ATTLIST entry key CDATA #REQUIRED>";
private static final String EXTERNAL_XML_VERSION = "1.0";

定义属性的 XML 该如何写已经一目了然了,关键是 http://java.sun.com/dtd/properties.dtd 给我们的定义,也可以直接抓下它的定义来

➜ / curl -L http://java.sun.com/dtd/properties.dtd
<!--
Copyright 2006 Sun Microsystems, Inc. All rights reserved.
-->

<!-- DTD for properties -->

<!ELEMENT properties ( comment?, entry* ) >

<!ATTLIST properties version CDATA #FIXED "1.0">

<!ELEMENT comment (#PCDATA) >

<!ELEMENT entry (#PCDATA) >

<!ATTLIST entry key CDATA #REQUIRED>

根节点是 properties, 其下是多个 entry 定义,entry 的 key 属性即属性的 Key, entry 中是 CDATA 文本内容就是属性值。下面是一个实际的属性 XML 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties PUBLIC "xml property dtd" "http://java.sun.com/dtd/properties.dtd">

<properties>
    <entry key="select.user">
select id, firstname, lastname, address
  from user
  where id = ?
    </entry>
    <entry key="update.user">
<![CDATA[
update user set address='<where>'
  where id = ?
]]>
    </entry>
</properties>

如果 entry 中有 XML 的特殊字符就用 <![CDATA[...]> 框起来,无 XML Entities 就无所谓了。

现在要引用定义在 XML 中的属性值就简单了,没有什么特别的,不需要额外的文件解析类,只是文件扩展名的不同而已。比如在你的 Spring 代码中使用如下注解

@PropertySource("sql/queries.sql.xml")   //由于默认是 classpath 所以无需写成 @PropertySource("classpath:sql/queries.sql.xml")
public class JavaConfig {
......
}

如此,定义在 sql/queries.sql.xml 便进到属性环境中去了,在任何一个 Spring Bean 中可以用 @Value("${select.uer}") 引用到了

@Named
public class UserRepository {

    @Value("${select.user}")
    private String selectUserSql;

    ........

从此再也不用那个 SqlPropertySourceFactory 了。

IDE 打开 queries.sql.xml 只会以 XML 文件格式来进行语法高亮,如果想要 IDE 对其中的 SQL 语法进行高亮显示,可以修改 IDE 的配置,让 SQL 文件类型也包含 *.sql.xml 这个模式的文件名,那么每次在 IDE 中打开后缀为 *.sql.xml 都会认成是 SQL 文件,更增添了可读性。

类别: Spring. 标签: . 阅读(99). 订阅评论. TrackBack.

Leave a Reply

Be the First to Comment!

avatar
wpDiscuz