【SQL开源代码栏目提醒】:网学会员--在 SQL开源代码编辑为广大网友搜集整理了:扩展 iBatis 以透明支持多种数据库 - 编程语言绩等信息,祝愿广大网友取得需要的信息,参考学习。
扩展 iBatis 以透明支持多种数据库 iBatis 是一个
开源的对象关系映射框架着重于 POJO 与
SQL 之间的映射关系。
和其它 ORM 框架不同iBatis 开发者需要自己编写和维护
SQL 语句。
为了得到更好的执行性能在实际开发中免不了会使用一些数据库方言。
随之而来的一个问题是如何在增加对新的数据库支持的同时尽可能避免对已有应用程序
代码的修改本文提供了一个简单有效的方法通过扩展 iBatis 来透明地支持多数据库方言。
iBatis 简介 iBatis 是一个
开源的对象关系映射程序着重于 POJO 与
SQL 之间的映射关系。
使用时开发者提供一个被称为
SQL 映射的 XML 文件定义程序对象与
SQL 语句间的映射关系 iBatis 会根据
SQL 映射文件的定义运行时自动完成
SQL 调用参数的绑定以及 JDBC ResultSet 到 Java POJO 之间的转换。
下面是一个简单的例子相比其它 ORM 工具iBatis 相对简单更容易上手。
清单 1. POJO 对象 public class BlogData implements Serializable protected String id protected String name protected int rating 0 public String getId return id public String getName return name public int getRating return rating public void setIdString id this.id id public void setNameString name this.name name public void setRatingint rating this.rating rating 清单 2.
SQL 映射文件—— SQLMAP.XML insert into blogsid name rating valuesid name rating update blogs set name name rating rating where id id delete from blogs where id id select from blogs order by rating desc fetch first size rows only 清单 3. iBatis 配置文件—— SQLMAPCONFIG.XML //
SQL 映射声明 清单 4.
SQL 访问示例 String cfg SQLMAPCONFIG.XML Reader r Resources.getResourceAsReadercfg SqlMapClient client new SqlMapConfigParser.parser ... // 插入 BlogData o new BlogData o.setIdid o.setNametest client.insertSAVEBLOG o ... // 更新 o.setRating10 client.updateUPDATEBLOG o ... // 删除 client.deleteREMOVEBLOG id ... // 查询 Map params new HashMap params.putsize 5 List l client. queryForListGETMOSTPOPULARBLOG params 0 5 ... iBatis 应用中的多数据库支持 在 iBatis 应用中开发者仍需自己编写具体的
SQL 语句iBatis 只是隐藏和简化了 JDBC 的相关调用。
实际开发中我们不免需要就
SQL 语句针对各种特定的数据库进行特殊优化以期获取更好的执行性能随之而来的一个问题是如何应对新的数据库平台支持的需求。
一般的做法是修改
SQL 映射文件提供一些新的针对新数据库平台的
SQL 语句版本然后修改程序
代码添加相应调用。
继续上面的例子。
上面的例子中对于
SQL 语句 GETMOSTPOPULARBLOG 的定义我们使用了 DB2 特有的
SQL 方言“FETCH FIRST n ROWS ONLY”对于这样的程序如果希望增加对 MYSQL 的支持按照一般的做法需要 1修改 SQLMAP.XML增加语句定义“GETMOSTPOPULARBLOG_MYSQL”。
清单 5. 增加语句定义 ...... select from blogs order by rating desc fetch first size rows only select from blogs order by rating desc limit 0 size ...... 2搜索程序
代码在每一个调用 iBatis “GETMOSTPOPULARBLOG”的地方增加检测 MYSQL 数据库引擎的
代码并添加对“GETMOSTPOPULARBLOG_MYSQL”的 iBatis 调用。
清单 6. 增加检测数据库引擎的
代码 ...... SqlMapClient client ... ...... // 查询 Map params new HashMap params.putsize 5 List l null Connection conn client.getCurrentConnection String prodName conn.getMetaData.getDatabaseProductName.toLowerCase if prodName.indexOfmysql - 1 //Microsoft
SQL Server l client. queryForListGETMOSTPOPULARBLOG_MYSQL params else l client. queryForListGETMOSTPOPULARBLOG params ...... 每增加一个新的数据库支持增加了一些新的针对新数据库平台的
SQL 语句版本我们就不得不搜索源
代码找出所有受到影响的 iBatis 调用修改并增加针对新数据库的特殊调用。
代码维护时每次涉及使用数据库方言的
SQL 语句我们也都必须记住小心谨慎地处理所有相关的数据库特化调用。
这样的工作乏味且容易出错。
本文我们试图在分析 iBatis 源码的基础上通过适当扩展 iBatis提供一个高效方便的解决方案。
扩展 SqlMapConfigParser 在 iBatis 应用中SqlMapConfigParser 负责解析 iBatis 配置文件加载所有的
SQL 映射文件生成 SqlMapClient 实例这是持久化调用的入口。
SqlMapConfigParser 的实现并不复杂。
成员函数 parser 将传入的配置文件 XML 输入流交给一个 XML 解析器。
XML 解析器解析 XML 输入并针对每一个 XML Fragment 调用合适的处理器处理。
所有的处理器都在 SqlMapConfigParser 类实例初始化时预先被注册到 XML 解析器上其中对于 iBatis 配置中的
SQL 映射声明只是简单地调用类 SqlMapParser 中的 parser 方法解析并加载相应的
SQL 映射定义文件。
清单 7. SqlMapConfigParser 实现 public class SqlMapConfigParser //XML 解析器 protected final NodeletParser parser new NodeletParser public SqlMapConfigParser ...... // 注册 XML 处理器 addSqlMapNodelets ...... // more public SqlMapClient parseReader reader ...... // 调用 XML 解析器解析传入的配置文件 XML 输入流 parser.parsereader return vars.client protected void addSqlMapNodelets //XML 处理器处理 XPath:/sqlMapConfig/sqlMap即
SQL 映射声明 parser.addNodelet/sqlMapConfig/sqlMap new Nodelet public void processNode nodethrows Exception Properties attributes NodeletUtils.parseAttributesnode String resource attributes.getPropertyresource Reader reader Resources.getResourceAsReaderresource new SqlMapParservars.parsereader // 调用 SqlMapParser.parser 方法 // 解析并加载
SQL 映射文件 ...... ...... 我们继承 iBatis 原有的配置文件解析器实现 SqlMapConfigParser重写其中对
SQL 映射声明的处理。
首先我们重写 SqlMapConfigParser 的成员函数 addSqlMapNodelets。
对于从 XML 解析器传入的
SQL 映射声明节点我们并不立即进行解析处理而只是将它们记录下来。
清单 8. 重写 addSqlMapNodelets 方法 public class SqlMapConfigParserEx extends SqlMapConfigParser List sqlMapNodeList new ArrayList ....... protected void addSqlMapNodelets //XML 处理器处理 XPath:”/sqlMapConfig/sqlMap”即
SQL 映射声明 parser.addNodelet/sqlMapConfig/sqlMap new Nodelet public void processNode nodethrows Exception sqlMapNodeList.addNodenode ...... 这些
SQL 映射声明被放到最后处理此时 SqlMapClient 实例已经基本构造完毕至少我们可以安全地调用它的相关方法打开数据库连接查询数据库引擎相关信息。
对于每个
SQL 映射声明SqlMapConfigParserEx 调用其成员函数方法 handleSqlMapNode 进行相应的
SQL 映射文件解析和加载处理数据库引擎支持的
SQL 方言版本信息作为参数被一并传入。
清单 9. 重写 parse 方法 public interface DialectMapping public String getDialectString productName // 返回数据库平台支持的
SQL 方言信息 public class SqlMapConfigParserEx extends SqlMapConfigParser List sqlMapNodeList new ArrayList DialectMapping dialectMapping ... ....... public SqlMapClient parseReader reader ...... super.parsereader String sqlDialect null SqlMapClient client vars.client Connection conn client.getDataSource.getConnection DatabaseMetaData dbMetaData conn.getMetaData String productName dbMetaData.getDatabaseProductName sqlDialect dialectMapping.getDialectproductName conn.close for Iterator iter sqlMapNodeList.iterator iter.hasNext handleSqlMapNodeNodeiter.next sqlDialect return client ...... 对于传入的
SQL 映射声明除了解析并加载
SQL 映射声明中指定的
SQL 映射文件handleSqlMapNode 还根据传入的
SQL 方言版本信息以一定的路径规则寻找针对该
SQL 方言的
SQL 映射文件定制版本将它们一并解析加载。
清单 10. handleSqlMapNode 方法 public interface SqlMapStreamMerger public void addInputInputStream input public InputStream merge ...... public class SqlMapConfigParserEx extends SqlMapConfigParser SqlMapStreamMerger sqlMapStreamMerger ... ....... public void handleSqlMapNodeNode node String sqlDialect Properties attributes NodeletUtils.parseAttributesnode String resource attributes.getPropertyresource // 读取
SQL 映射声明指定的
SQL 映射文件 InputStream is Resources.getResourceAsStreamresource sqlMapStreamMerger.addInputis // 寻找并试图读取针对
SQL 方言 sqlDialect 的
SQL 映射文件定制版本 int idx resource.lastIndexOf/ resource resource.substring0 idx / sqlDialect resource.substringidx is Resources.getResourceAsStreamresource if is null sqlMapStreamMerger.addInputis // 将读取到的
SQL 映射文件包括基本的
SQL 映射文件以及为特定数据库的定制版本 // 合成一个
SQL 映射文件 XML 数据流交给 SqlMapParser 解析处理 Reader reader new InputStreamReadersqlMapStreamMerger.merge new SqlMapParservars.parsereader ...... 成员函数 handleSqlMapNode 将找到的
SQL 映射文件包括
SQL 映射声明中指定的基本的
SQL 映射文件以及以一定路径规则找到的针对特定数据库的
SQL 映射文件定制版本通过 SqlMapStreamMerge 对象整合成一个
SQL 映射文件才递交给 SqlMapParser 解析处理。
SqlMapStreamMerge 确保 1. 结果
SQL 映射文件中不存在重复 ID 的
SQL 映射配置块statement、insert、update、delete、
sql、resultMap 等。
如果基本的
SQL 映射文件与针对特定数据库的
SQL 映射文件定制版本之间存在重复 ID 的
SQL 映射配置块定义SqlMapStreamMerge 保留后者覆盖前者 2. 结果
SQL 映射文件中的配置块按引用依赖顺序有序排列。
即所有的 resultMap 声明都位于引用它们的 statement 声明之前被继承的 resultMap 声明都位于继承的 resultMap 声明之前等。
先 Merge 再解析这是必要的因为 SqlMapParser 本身并不支持
SQL 映射定义的方法重写。
使用 使用扩展的 SqlMapConfigParser 实现 —— SqlMapConfigParserEx可以大大简化应用程序中多数据库支持问题的解决。
还是之前那个例子。
首先我们使用 SqlMapConfigParserEx 替换程序中的 SqlMapConfigParser 使用。
清单 11. 在应用
代码中使用扩展的 SqlMapConfigParserEx String cfg SQLMAPCONFIG.XML Reader r Resources.getResourceAsReadercfg // old code // SqlMapClient client new SqlMapConfigParser.parser // new code SqlMapClient client new SqlMapConfigParserEx.parser ... 现在要增加对 MySQL 的支持只需建立一个新的
SQL 映射文件 /mysql/SQLMAP.XML重写 SQLMAP.XML 中 GETMOSTPOPULARBLOG 的
SQL 定义。
Java
代码可以继续保持数据库平台透明性无需作出任何修改。
清单 12. 针对 MySQL 的配置文件 /mysql/SQLMAP.XML select from blogs order by rating desc limit 0 size 运行时SqlMapConfigParserEx 会自动检测数据库引擎版本信息读取文件 /mysql/SQLMAP.XML使用其中的针对 MYSQL 定制的GETMOSTPOPULARBLOG 定义覆盖 SQLMAP.XML 中的 DB2 方言版本从而确保程序行为的正确性。
我们支持针对新的数据库平台对
SQL 映射文件中的任意配置进行定制 / 重写甚至包括 、、 等尽管在实际应用中这样的需求并不常见。
关于 iBatis 2.1.5 上述分析只适合 iBatis 2.2.0 之后的版本。
在 iBatis 2.1.5 中addSqlMapNodelets 是 SqlMapConfigParser 的私有成员函数无法在子类中重写。
附件中我们给出了针对 iBatis 2.1.5 的 SqlMapConfigParserEx 实现大致思想类似这里就不再详述。
结束语 iBatis 作为一个 ORM 框架以其简单易用支持更为灵活的数据库 / 系统设计正在得到越来越多的关注。
iBatis 应用中开发者需要自己编写具体的
SQL 语句针对特定的数据库进行
SQL 优化处理跨数据库平台移植问题等。
本文针对 iBatis 应用中的多数据库支持问题通过扩展 iBatis 的现有实现给出了一个较为简单高效的解决方法。
上一篇:
C#城市公交查询系统(文献综述+外文文献翻译+源程序)
下一篇:
“海德格尔学案”对中国学人的启示