设为首页 收藏本站
查看: 1062|回复: 0

[经验分享] 项目中SQL语句文件保存及提取方法

[复制链接]

尚未签到

发表于 2018-10-21 12:49:15 | 显示全部楼层 |阅读模式
  第十九章 SQL文保存方法
  @http://blog.csdn.net/struts2/article/details/1745050
  在开发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语句。
  使用Class的getResourceAsStream方法可以获得对文件引用的InputStream。例如:文件目录结构:
  src
  com
  jpleasure
  dao
  SomeDao.java
  SomeDao.properties
  SomeDao.xml
  SomeDao.sql
  // properties

  public>  public InputStream getInputStream() {
  this.getClass().getClassLoader().
  getResourceAsStream("com/jpleasure/dao/SomeDao.properties");
  }
  }
  // xml

  public>  public InputStream getInputStream() {
  this.getClass().getClassLoader().
  getResourceAsStream("com/jpleasure/dao/SomeDao.properties");
  }
  }
  // txt(.sql)

  public>  public InputStream getInputStream() {
  this.getClass().getClassLoader().
  getResourceAsStream("com/jpleasure/dao/SomeDao.properties");
  }
  }
  Propertis文件
  Properties 文件是Java支持的标准的属性文件(相当于Windows对ini文件的支持)。
  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比较常用的格式为:
  

    Select
  *
  from
  dual   
  

    …
  
  
  使用XML格式的文件保存SQL需要自己写文件解析代码,由于Java对XML提供了内置的支持,并且第三方的开源库也很多,并且非常容易使用所以从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());
  // 获得根节点:
  Element element = doc.getRootElement();
  // 获得所有根节点的子节点: 节点列表
  List sqlNodeList = element.getChildren("sql");
  for (int i = 0; i < sqlNodeList.size(); i++) {
  Element sqlNode = (Element) sqlNodeList.get(i);
  // 获得SQL语句ID

  String>  // 获得SQL语句内容
  String sql = sqlNode.getTextTrim();
  map.put(id, sql);
  }
  } catch (JDOMException e) {
  e.printStackTrace();
  }
  return map;
  }
  使用XML文件格式保存SQL语句据的时候需要注意,SQL语句中的大于(>)小于(的时候)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>  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());

  >  sql = new StringBuffer();
  }

  >  } 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标志(相对与Properties和xml来说可能也不算是缺点),解析困难,需要较强的文本接卸的能力。
  通用SQL文件读取库
  如何设计一个同时支持三种文件格式的SQL文件读取库?

  首先我们抽象出一个SqlManager的接口,它提供通过Key获得对应SQL语句的操作。代码为:
  public interface SqlManager {
  /**
  * 获取指定Key的SQL语句
  *
  * @param key
  *            SQL语句的Key值
  * @return SQL语句内容
  * @throws SqlMgntException
  *             无法取得SQL语句时抛出此异常。
  */
  public String getSql(String key) throws SqlMgntException;
  }
  然后使用一个抽象基类,来定义通用的属性和操作,代码为:

  public abstract>  /* 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>  /* 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>  /**
  *
  */
  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语句装载在内存中。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-624505-1-1.html 上篇帖子: HTTP 错误 500.21 - Internal Server Error PageHandlerFactory-Integrated 下篇帖子: centos6.5 nfs-server 配置实例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表