`
kobexing933
  • 浏览: 120356 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

第十九章 SQL文保存方法

阅读更多

第十九章 SQL文保存方法

在开发JavaWeb应用程序的过程中可能经常需要使用到SQL语句来访问数据库。为了屏蔽SQL注入带来的危险,在Java中通常使用PreparedStatement,使用预编译的SQL语句。预编译的SQL语句是那些包含?的语句,使用PreparedStatement可以让数据库预先编译这些SQL模板,只有调用的时候套用必须的参数即可。

SQL文件的存放位置

那么在JavaWeb项目中预编译的SQL语句到底放在那里呢?

放在Java代码里肯定是不好的,为什么,有两点,第一,SQL语句放在Java代码里

太难看可,有不好的味道(参看Refactor),第二,每次SQL语句变更(可能经常发生)

都需要编译这些Java代码,比较烦。

那么SQL语句到底放在那里呢?根据这么多年的开发经验SQL文通常可以放在classes目录

下的文件中,存放SQL语句的文件有三种类型:properties文件,xml文件和txt文件。

在详细讨论文件格式之前,我们先讨论以下如何在Java类中得到这些文件的引用,以便从中得到需要的SQL语句。

使用ClassgetResourceAsStream方法可以获得对文件引用的InputStream。例如:文件目录结构:

src

com

jpleasure

dao

SomeDao.java

SomeDao.properties

SomeDao.xml

SomeDao.sql

// properties

public class SomeDao{

public InputStream getInputStream() {

this.getClass().getClassLoader().

getResourceAsStream("com/jpleasure/dao/SomeDao.properties");

}

}

// xml

public class SomeDao{

public InputStream getInputStream() {

this.getClass().getClassLoader().

getResourceAsStream("com/jpleasure/dao/SomeDao.properties");

}

}

// txt(.sql)

public class SomeDao{

public InputStream getInputStream() {

this.getClass().getClassLoader().

getResourceAsStream("com/jpleasure/dao/SomeDao.properties");

}

}

Propertis文件

Properties 文件是Java支持的标准的属性文件(相当于Windowsini文件的支持)。

Properties 文件的格式为:

# 注释

# 定义key,并且与key对应的值为value

key = value

可以使用java.util.Properties类来包装properties文件,使用如下

Properties props = new Properties();

try {

props.load(this.getInputStream());

} catch(IOException ioex) {

// 文件不存在,或者格式问题等

}

String value = props.getString("key")

注意上述格式中value不能换行,要想换行必须使用转义字符“\”。

在存储SQL的时候我们使用如下的格式,例如:

# 某业务,某操作SQL

xxx_0001 = select * from dual

# 某业务,其他操作SQL

xxx_0002 = \

select \

* \

from \

dual

使用Properties文件的优点是:Java内置支持布需要手写文件解析代码,另外使用也非常简单。

缺点是:SQL语句不能直接编写,需要追加转义字符"\",无法将这样的SQL语句直接拷贝到数据库客户端中运行。

XML文件

使用XML文件保存SQL比较常用的格式为:

<sqls>

<sql id=”xxx_0001”>

Select

*

from

dual <!-- 文件注释 -->

</sql>

<sql id=”xxx_0001”>

</sql>

</sqls>

使用XML格式的文件保存SQL需要自己写文件解析代码,由于JavaXML提供了内置的支持,并且第三方的开源库也很多,并且非常容易使用所以从xml文件中解析SQL语句也没有什么困难。以下以jdom为例讲解如何解析上述的XML格式的SQL文件。

protected Map analysis() {

Map map = new HashMap();

DOMBuilder builder = new DOMBuilder();

Document doc = null;

try {

// 解析XML文件

doc = builder.build(this.getInputStream());

// 获得根节点: <sqls>

Element element = doc.getRootElement();

// 获得所有根节点的子节点: <sql>节点列表

List sqlNodeList = element.getChildren("sql");

for (int i = 0; i < sqlNodeList.size(); i++) {

Element sqlNode = (Element) sqlNodeList.get(i);

// 获得SQL语句ID

String id = sqlNode.getAttribute("id").getValue();

// 获得SQL语句内容

String sql = sqlNode.getTextTrim();

map.put(id, sql);

}

} catch (JDOMException e) {

e.printStackTrace();

}

return map;

}

使用XML文件格式保存SQL语句据的时候需要注意,SQL语句中的大于(>)小于(<)XML文件的格式冲突,有两种解决方法,

第一:使用全角的大于()小于()号。

第二:使用<![CDATA[ ]]>来包围所有的内容。

另外,通常情况下(不使用<![CDATA[ ]]>的时候)SQL的注释只能使用XML的注释格式(<!-- -->)。

使用XML格式的文件的有点:SQL语句可以正常书写,文件解析相对简单。

缺点:大于号,小于号的冲突;无法添加SQL注释(通常只能使用XML格式的注释)

TXT文件(以.SQL为后缀)

我们先说一下TXT文件(.sql文件)的格式

------------------------------------

--@ SQL-1

------------------------------------

SELECT

SYSDATE

FROM

DUAL(SQL)

------------------------------------

--@ SQL-2

-- 某某用途的SQL

------------------------------------

SELECT

SYSDATE, ROWID -- 某某字段

FROM

DUAL(SQL) -- 某某表

使用txt格式的文件,非常的简单,和一般的写SQL语句一样,可以使用任何的SQL标准语法。只是有一个地方需要注意,就是每个SQL语句的头注释的地方加上一行特殊的内容用来标记SQL语句的ID

--@ SQL-1

这样的文件解析比较困难,但是也不是不能做,解析代码如下:

protected Map analysis() {

Map sqlMap = new HashMap();

String id = null;

StringBuffer sql = new StringBuffer();

if (this.getInputStream() != null) {

BufferedReader sqlFileReader = null;

try {

sqlFileReader = new BufferedReader(

new InputStreamReader(this.getInputStream()));

String currentLine = null;

// 逐行读取文件内容

while ((currentLine = sqlFileReader.readLine()) != null) {

// 发现新的SQL语句,将已经发现的SQL语句放在SQL容器中

if (currentLine.startsWith("--@")) {

if (id != null) {

sqlMap.put(id, sql.toString());

id = null;

sql = new StringBuffer();

}

id = currentLine.substring("--@".length()).trim();

} else if (currentLine.startsWith("--")) {

// 不读取注视行

continue;

} else {

// 非注释,ID

// 去掉SQL语句行的末尾注视

if (currentLine.length() > 0) {

int commentsIndex = currentLine.indexOf("--");

if (commentsIndex > 0) {

currentLine = currentLine.substring(0,

commentsIndex);

}

// 将换行符替换为空格

sql.append(currentLine + " ");

} else {

// 将换行符替换为空格

sql.append(" ");

}

}

currentLine = null;

}

if (id != null) {

sqlMap.put(id, sql.toString());

}

} catch (IOException ioex) {

ioex.printStackTrace();

} finally {

if(sqlFileReader != null) {

try {

sqlFileReader.close();

} catch (IOException e) {

}

}

}

}

return sqlMap;

}

使用TXT格式的文件的有点:SQL语句书写非常方便,可以拷贝出来直接运行,可以使用标准的SQL注释格式。

缺点:每个SQL语句必须添加特殊的ID标志(相对与Propertiesxml来说可能也不算是缺点),解析困难,需要较强的文本接卸的能力。

通用SQL文件读取库

如何设计一个同时支持三种文件格式的SQL文件读取库?

<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:414.75pt; height:245.25pt'> <v:imagedata src="file:///C:\DOCUME~1\mazhao\LOCALS~1\Temp\msohtmlclip1\01\clip_image001.jpg" o:title="sqlwrapper" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

首先我们抽象出一个SqlManager的接口,它提供通过Key获得对应SQL语句的操作。代码为:

public interface SqlManager {

/**

* 获取指定KeySQL语句

*

* @param key

* SQL语句的Key

* @return SQL语句内容

* @throws SqlMgntException

* 无法取得SQL语句时抛出此异常。

*/

public String getSql(String key) throws SqlMgntException;

}

然后使用一个抽象基类,来定义通用的属性和操作,代码为:

public abstract class AbstractSqlManager implements SqlManager {

/* SQL文件输入流 */

private final InputStream is;

/**

* SQL文件输入流获取方法。

*

* @return SQL文件输入流

*/

public InputStream getInputStream() {

return is;

}

/**

* 默认构造方法

*

* @param is

* SQL文件输入流

*/

public AbstractSqlManager(InputStream is) {

this.is = is;

}

/* SQL语句容器 */

private Map sqlContainer = null;

/* 同步KEY */

private final Object syncKey = new Object();

/**

* 通过SQL语句ID获取SQL语句内容。

*

* @param key

* SQL语句ID

* @return SQL语句

*/

public String getSql(final String key) throws SqlMgntException {

synchronized (syncKey) {

if (sqlContainer == null) {

this.sqlContainer = initContainer();

}

}

return (String) sqlContainer.get(key);

}

/**

* 抽象的模板方法,用来解析不同类型的SQL文件。

* @return 配对的SQL内容

*/

protected abstract Map initContainer();

}

注意其中的抽象方法:

protected abstract Map initContainer();

通过这个抽象方法把SQL文件的解析交给了具体的实例(模式参看:模板方法)

三个具体的SqlManager类我们不必具体说了,都是对上述抽线的解析方法的实现。

工厂类可以根据对应文件的后缀明创建对应的SqlManager。代码为:

public class SqlManagerFactory {

/* XMl文件类型 */

public static final String TYPE_XML = ".xml";

/* SQL文件类型 */

public static final String TYPE_SQL = ".sql";

/* 属性文件类型 */

public static final String TYPE_PROP = ".properties";

// begin ma.zhao@dl.cn 2006/03/09

// add singleton map for SqlManager

private static Map sqlManagerContainer = new HashMap();

// end ma.zhao@dl.cn 2006/03/09

/**

* 创建对应的SqlManager实现类

*

* @param filePath

* SQL文件相对路径和文件名

* @return

*/

public static SqlManager createSqlManager(String filePath) throws SqlMgntException {

SqlManager manager = null;

synchronized (sqlManagerContainer) {

manager = (SqlManager) sqlManagerContainer.get(filePath);

// 如果SqlManager不存在,则根据文件初始化SqlManager

// 并且将它放在SqlManager容器中

if (manager == null) {

InputStream is = SqlManagerFactory.class.getClassLoader()

.getResourceAsStream(filePath);

if(is == null) {

throw new SqlMgntException(

"Sql File Not Fount! Input file name path is:" + filePath);

}

if (filePath.endsWith(TYPE_XML)

|| filePath.endsWith(TYPE_XML.toUpperCase())) {

manager = new SqlManagerXmlImpl(is);

} else if (filePath.endsWith(TYPE_SQL)

|| filePath.endsWith(TYPE_SQL.toUpperCase())) {

manager = new SqlManagerSqlImpl(is);

} else if (filePath.endsWith(TYPE_PROP)

|| filePath.endsWith(TYPE_PROP.toUpperCase())) {

manager = new SqlManagerPropImpl(is);

} else {

throw new SqlMgntException(

"Sql File Type Not Support, Input file path is:" + filePath);

}

//

if (manager != null) {

sqlManagerContainer.put(filePath, manager);

}

}

}

return manager;

}

}

注意上述代码可以控制每个对应的SQL文件始终只有一个对应的SqlManager,不会对同一个文件创建多个SqlManager类。

所有上述过程中的异常都以SqlMgntException的方式向上抛出,代码为:

public class SqlMgntException extends Exception {

/**

*

*/

private static final long serialVersionUID = 1L;

public SqlMgntException() {

super();

// TODO Auto-generated constructor stub

}

public SqlMgntException(String message, Throwable cause) {

super(message, cause);

// TODO Auto-generated constructor stub

}

public SqlMgntException(String message) {

super(message);

// TODO Auto-generated constructor stub

}

public SqlMgntException(Throwable cause) {

super(cause);

// TODO Auto-generated constructor stub

}

}

优化的方向,

1 初始话加载所有的SQL语句

程序运行之初,将所有的SQL语句装载在内存中。

分享到:
评论

相关推荐

    VB开发经验技巧宝典

    第19章 Windows 应用 第20章 应用程序控制 第21章 数据库技术 第22章 SQL查询相关技术 第23章 打印与报表技术 第24章 图表技术 第25章 网络开发技术 第26章 Web编程 第27章 安全...

    Oracle SQL高级编程(资深Oracle专家力作,OakTable团队推荐)--随书源代码

    第3章 访问和联结方法 55 3.1 全扫描访问方法 55 3.1.1 如何选择全扫描操作 56 3.1.2 全扫描与舍弃 59 3.1.3 全扫描与多块读取 60 3.1.4 全扫描与高水位线 60 3.2 索引扫描访问方法 65 3.2.1 索引结构 66 ...

    C++ GUI Programming with Qt 4 中文版(第一章至第十章).doc

    第九章:网络编程 介绍Qt的网络模块,学习如何编写客户端和服务器应用程序,包括HTTP、FTP和TCP/IP通信。 第十章:国际化和本地化 讨论如何使应用程序支持多语言,包括资源文件的管理和QTranslator类的使用,以实现...

    django中文开发手册

    #### 十九、安全(第十九章) - **防止跨站脚本攻击**:介绍如何使用Django的安全机制来防范XSS攻击。 - **防止SQL注入**:讲解Django如何通过ORM来避免SQL注入风险。 - **安全最佳实践**:总结Django开发过程中...

    PLSQL用户指南与参考中文PDF版

    #### 第一章:PL/SQL 一览 **一、理解PL/SQL的主要特性** 1. **块结构**:PL/SQL 是一种结构化编程语言,其基本单位是块。一个完整的 PL/SQL 块包括声明部分、执行部分和异常处理部分。这种结构有助于清晰地组织...

    c++ Builder+实例入门陈雪飞清晰版

    第19章 FTP客户端 19.1 基础知识 19.2 编程思路 19.3 操作步骤 19.4 创意与超越 19.5 本章小结 附录A C++语法基础 A.1 C++的基本数据类型及常量、变量 A.1.1 常用的基本数据类型 A.1.2 ...

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    Java Web编程宝典-十...第19章 体验完美视觉盛宴 第3篇 项目实战篇 第20章 通讯录开发模板 第21章 打造自己的视频网站 第22章 自我展示的空间 第23章 技术交流的平台 第4篇 商业系统篇 第24章 基于SSH2的天下淘商城

    MyEclipse 6 Java EE 开发中文手册

    #### 第十五章:开发Spring+Struts+Hibernate应用 **15.1 构建Spring+Struts+Hibernate应用** - **创建综合项目**:在MyEclipse中创建新的综合项目。 - **配置环境**:设置Spring、Struts和Hibernate所需的库文件...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part2

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part5

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part4

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part3

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    AJAX基础概念、核心技术与典型案例(内涵动态实例)

    第19章(/ch19) 程序描述:本章模仿Google ig,使用Ajax技术实现一个个性化主页系统。通过拖拽方式设置主页,当完成拖拽窗口时,自动保存布局,使用户可以随时根据喜好来设置布局。 /login.jsp 登录页面 /...

Global site tag (gtag.js) - Google Analytics