当前页面位置: » 丰搜网 » 文档中心 » 详细内容
使用sqlmaps进行对象关系映射
使用sqlmaps进行对象关系映射作者:sunil patil翻译:yuchen(yuyu1984)版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:
sunil patil;yuchen(yuyu1984)
原文地址:
http://www.on
java.com/pub/a/on
java/2005/02/02/sqlmaps.htm
中文地址:
http://www.matrix.org.cn/resource/article/43/43822_sqlmaps.
html关键词: sqlmaps oo mapping
引论随着
hibernate逐渐成为其他架构之首,现今的许多工作都是在对象关系(or)映射域上进行。但是,那些对象关系映射工具有一个问题:大部分
数据库管理员似乎对这些对象关系(or)映射工具所产生的查询不放心。不幸的是,这些
数据库管理员不了解,架构所产生的查询是多么的优越,并且使你的应用程序更加灵活。他们觉得随着
数据库成为应用程序的主要瓶颈,应该完全控制sql查询,以便他们能分析和调试以促进性能。
但有个问题是,如果不使用对象关系(or)映射工具,那么你将花费很多资源在书面形式上和维护低级jdbc代码。由于以下原因,每个jdbc应用程序都将含有重复代码:
1.连接和事务管理。
2.设置
java对象为查询参数。
3.转换sql resultsets 为
java 对象。
4.生成查询字符串。
ibatis 的 sqlmaps 架构帮助我们在相当大的程度上减少了正常情况下访问关系
数据库的
java代码的总量。它考虑到了当前三个比较关心的问题,所以它可以将一个简单的
javabean 对象映射到 preparedstatement 参数和 resultset 值。sqlmaps 的原理其实很简单:提供一个简单的架构来提供80%的jdbc功能。
这篇文章是关于如何使用 sqlmaps 架构的进阶辅导。我们将建立一个简单的
struts 应用程序并且用 sqlmaps 配置它为开始。接着,我们将演示如何执行基本的
数据库操作比如 select, insert, update 等等。然后,我们将演示 sqlmaps 为连接和事务管理提供的操作。在最后,我们将试着使用一些高级的 sqlmaps 功能部件比如缓存和
分页。
sqlmaps 的基本思想使用 sqlmaps 架构,你必须建立一个
xml文件列出所有你希望在应用程序中执行的sql查询。对于每个sql查询,你来指定哪个
java类可以交换参数和resultset(结果集)。
在你的
java代码中,如果你想要执行一个特殊的查询,你需要创建一个对象来传递查询参数和必要的条件,然后传递这个对象和查询的名字让 sqlmaps 执行。一旦查询被执行,sqlmaps 将为你指定的接受查询结果的类创建一个句柄,并且将它和
数据库返回的resultset 中的值存储在一起。
一个简单的使用sqlmaps的应用程序(hello world)我们将创建一个简单的
struts应用程序开始来演示什么是使用sqlmaps的应用程序所需要的。这个例子的代码是来自以下资源部分。在这个例子中,我们用一个
jsp页面要求用户输入contactid属性。一旦它被提交,我们用它在contact 表中搜索一个连接,并且用另一个
jsp页面展示给用户。接下来一步一步说明:
1. 拷贝ibatis-sqlmap-2.jar 和 ibatis-common-2.jar 到你的
web-inf/lib 文件夹中。
2. 在你的
java源文件夹中创建一个 sqlmapconfig.
xml 文件,像这样:
<sqlmapconfig>
<settings usestatementnamespaces="false" />
<transactionmanager type="jdbc">
<datasource type="simple" >
<property name="jdbc.driver"
value="com.ibm.db2.jdbc.app.db2driver"/>
<property name="jdbc.connectionurl"
value="jdbc:db2:sample"/>
<property name="jdbc.username"
value="db2admin"/>
<property name="jdbc.password"
value="admin2db"/>
</datasource>
</transactionmanager>
<sqlmap resource="contact.xml"/>
</sqlmapconfig>
sqlmapconfig.
xml 是sqlmaps的部署描述信息,包括以下元素:
<sqlmapconfig>是文件的根元素。<settings>被用来定义应用程序级别的设置;例如 usestatementnamespaces 属性被用来定义是否要用准备说明的全限定名。它可以有一些另外的属性用于控制缓存和初始化;要知道更进一步的细节请查看文档。
<transactionmanager> 被用来定义你想要在你的应用程序中使用的事务管理。在我们的例程中,我们要用connection 对象的 commit 和 rollback 方法来管理事务,所以我们用jdbc作为事项管理程序。它包含 <datasource> 作为子元素,<datasource>用来定义你要使用的连接管理的类型。在我们的例程中,我们要用sqlmaps自带的连接池实现,如此我们将使用一个simple类型的数据源。为了建立连接池,sqlmaps要求像jdbc驱动名称,url,和密码这些信息,因此,我们使用<property>元素来传递这些信息。稍后,我们将更加详细地讨论各种各样可用的事务和连接管理选项。
<sqlmap>元素被用来表明 sqlmap 配置文件。这些文件,如早先讨论的,列出了你希望执行的sql查询。
3. 创建一个
javabean类,contact.
java,有属性 firstname, lastname 和 contactid 和相应的get、set方法。这个类将被用来向resultset传递查询参数并读取值。
public class contact implements serializable{
private string firstname;
private string lastname;
private int contactid;
//getter setter methods for firstname,
//lastname and contactid property
}4. 建立一个contact.
xml文件,我们将在文件中列出所有要执行的与表contact有关的sql查询。
<sqlmap namespace="contact"">
<typealias alias="contact"
type="com.sample.contact.contact"/">
<select id="getcontact"
parameterclass="int" resultclass="contact"">
select contactid as contactid,
firstname as firstname,
lastname as lastname from
administrator.contact where contactid = #id#
</select>
</sqlmap>
文件中使用的标签如下:
<sqlmap>是文件的根元素。正常情况下,你的应用程序将有不止一个表,由于你要把与不同表有关的查询分开成不同的名称空间(namespace),<namespace> 元素就是被用来指定在此文件中查询应被放置的名称空间。
<typealias> 用来说明contact 类的全限定名的一个简称。在此说明后,这个简称可以被用来替代全限定名。
在sqlmaps架构中 <select> 元素用来声明sqlmaps架构中的select查询。它的值就是你能指定的要执行的查询。id属性被用来指定通知sqlmaps 执行特殊查询的名称。parameterclass 被用来指定传递查询参数的类,resultclass 提供从resultset返回值的类的名称。
5. 在action类的 execute() 方法内,我们建立了一个sqlmapclient的句柄,它被用来和sqlmaps相互作用。我们必须向sqlmapclientbuilder传递sqlmapconfig.
xml文件,它被用来读取配置设置。
dynaactionform contactform =
(dynaactionform)form;
reader configreader =
resources.getresourceasreader("sqlmapconfig.xml");
sqlmapclient sqlmap =
sqlmapclientbuilder.buildsqlmapclient(configreader);
contact contact = (contact)
sqlmap.queryforobject("getcontact",
contactform.get("contactid"));
request.setattribute("contactdetail", contact);
return mapping.findforward("success");
当你要执行一个select 查询时,应该使用 sqlmaps 的 queryforobject 方法。在contact.
xml文件中,我们已经指定parameterclass为int,所以我们在传递查询的名称的时候传递一个integer作为contactid (i.e getcontact)。
sqlmaps 将返回一个contact 类的对象。
基本数据库操作现在我们要返回我们的所关注的使用sqmlaps如何表示一些基本的
数据库操作。
1. insert操作 我们从如何执行一个开始insert操作开始。
<insert id="insertcontact" parameterclass="contact">
insert into administrator.contact( contactid,firstname,lastname)
values(#contactid#,#firstname#,#lastname#);
</insert>
<insert> 元素被用来声明一个insert的sql查询。它有一个parameterclass 属性用来指明哪个
javabean类将被用来传递(request)请求参数。在插入新的记录时,我们要使用contactid属性的值,所以我们必须在sql查询中使用一个#contactid#。
public void contactinsert() throws sqlexception, ioexception {
sqlmap.starttransaction();
try {
sqlmap.starttransaction();
contact contact = new contact();
contact.setcontactid(3);
contact.setfirstname("john");
contact.setlastname("doe");
sqlmap.insert("insertcontact",contact);
sqlmap.committransaction();
} finally{
sqlmap.endtransaction();
}
}在我们的
java代码中,我们建立了一个contact 对象,存放它的值,然后调用sqlmap.insert()方法,传递我们要执行的查询的名称和 contact 。这个方法将插入一个新的contact并且返回新插入的contact的主键。
缺省情况下,sqlmaps 把每个dml方法当作工作的一个单元。但是你能使用 starttransaction, committransaction 和 endtransaction 方法来划分事务处理。通过调用 starttransaction() 方法你可以启动一个事务处理,此方法也能从连接池中获得一个连接。在这个事务中,这个连接对象将被用来执行查询。如果这个事务的所有查询都被成功执行,应该调用 committransaction 方法提交你的改动。不考虑你的事务成功与否,最后都要调用 endtransaction 方法,将连接对象返回连接池,同时,此方法对于恰当的清理工作是必要的。
2.update<update>元素被用来声明一个更新查询。它的 parameterclass 元素被用来声明传递查询参数的
javabean 的名称。在你的
java代码中,你可以用sqlmap.update("updatecontact",contact)指示 sqlmaps 来触发一个update查询。这个方法将返回被更改行的数目。
<update id="updatecontact" parameterclass="contact">
update administrator.contact set
firstname=#firstname# ,
lastname=#lastname#
where contactid=#contactid#
</update>
3.delete<delete> 元素被用来声明一个delete操作。在你的
java类中,执行如下操作: sqlmap.delete("deletecontact",new integer(contactid)) 方法返回被更改行的数目。
<delete id="deletecontact" parameterclass="int">
delete from administrator.contact where contactid=#contactid#
</delete>
4.procedureprocedure 元素支持
存储过程。大部分
存储过程要一些类似in, inout 或者 out 的参数。所以,创建一个 <parametermap> 元素并列出你要向
存储过程传递的参数。只有当参数类型为out或者inout时parametermap 对象才会被改变。
<parametermap id="swapparameters" class="map" >
<parameter property="contactid" jdbctype="integer"
javatype="java.lang.integer" mode="in"/>
<parameter property="firstname" jdbctype="varchar"
javatype="java.lang.string" mode="in"/>
<parameter property="lastname" jdbctype="varchar"
javatype="java.lang.string" mode="in"/>
</parametermap>
<procedure id="swapcontactname" parametermap="swapparameters" >
{call swap_contact_name (?, ?,?)}
</procedure>
你的代码一开始就要创建一个你要传递给
存储过程的参数的 hashmap ,然后把它和你要执行的查询的名称一同传递给 sqlmap 。
hashmap parammap = new hashmap();
parammap.put("contactid", new integer(1));
parammap.put("firstname", "sunil");
parammap.put("lastname", "patil");
sqlmap.queryforobject("swapcustomername", parammap);
连接和事务管理sqlmaps 架构已经为你考虑倒了连接管理。缺省情况下,它附带有连接管理的3种不同的实现。你可以通过<datasource> 元素的类型(type)属性的值指定你要使用的实现。
simple: 使用sqlmaps 自带的连接池实现。当使用这个实现时,我们必须传递连接信息(比如jdbc驱动名,用户名,密码)给sqlmaps。
dbcp: 使用apache 的 dbcp 连接池算法。
jndi: 使用一个容器提供数据源。如果你要使用这个方法,首先配置容器中的jdbc数据源(用一些特殊的方法),然后像这样指定数据源的jndi名:
<transactionmanager type="jdbc" >
<datasource type="jndi">
<property name="datasource"
value="java:comp/env/jdbc/testdb"/>
</datasource>
</transactionmanager>
datasource 特性的值应该指向你要使用的数据源的 jndi 名称。
sqlmaps 使用 datasourcefactory 来实现连接管理,所以你可以建立你自己的类实现这些接口并通知sqlmaps 使用它,如果你愿意的话。
对于事务管理,sqlmapconfig.
xml文件中 <transactionmanager> 元素的值指明了哪个类将被用来处理事务管理。
jdbc: 在这种情况下,通过调用connection 对象的begin和commit方法来控制事务。这一选择被用在容器外运行的应用程序上并且和单独的
数据库相互作用。
jta: 在此情况下,使用一个全局的jta事务。 sqlmaps 的活动只是一个广范的可能涉及其他
数据库和事务源的事务的一部分。
external: 在此情况下,你要自己管理事务。最为架构生存周期的一部分,事务是不会被提交或回滚的。这个设置对只读
数据库很有用。
高级特征现在,我们花点时间讨论一下sqlmaps 架构的高级特征。这篇文章的范围不允许我覆盖全部的特征,所以我将讨论一些我认为经常用到的内容;你可以查看sqlmaps文档查找其所支持的特征。
caching<cachemodel> 元素被用来描述一个使用(query-mapped)查询映射语句的缓存。
12345678901234567890123456789012345678901234567890
<cachemodel id="contactcache" type="lru">
<flushonexecute statement="insertcontact"/>
<flushonexecute statement="updatecontact"/>
<flushonexecute statement="deletecontact"/>
<property name="size" value="1000"/>
</cachemodel>
<select id="getcachedcontact" parameterclass="int"
resultclass="contact" cachemodel="contactcache">
select firstname as firstname,lastname as lastname
from contact where contactid = #contactid#
</select>
每个查询都有一个不同的缓存模型,或者几个查询共享一个缓存。sqlmaps 提供不同缓存类型的可插入的架构。cachemodel 元素的type属性指明了应该使用的实现。
lru: 当缓存满了的时候,从缓存中移出最少用到的元素。
fifo: 当缓存满了的时候,将建立时间最长的对象移出缓存。
memory: 使用
java引用类型像soft, weak 和 strong 来管理缓存特性。它允许垃圾收集器来决定是驻留在内存还是被销毁。这种实现应该使用在可用内存少的应用程序中。
oscache: 一个oscache2.0缓存引擎的插件。需要将oscache.properties放在根文件夹中来配置oscache。这个实现可以被用到分布式应用程序中。
<select>元素的cachemodel属性定义哪个缓存模型用来存放结果。通过设置<settings>的属性cachemodelsenabled的值为false可以禁止sqlmapclient 全部的缓存。
如何允许记录日志sqlmaps通过使用jakarta commons logging架构来提供记录日志信息,下面几步介绍如何允许记录日志:
1. 添加log4j.jar到你的应用程序的classpath所设置的目录中。对于一个
web应用程序,你将需要拷贝此文件到
web-inf/lib目录中。
2. 在你的应用程序的classpath所设置的目录中,建立一个log4j.properties文件如下:
log4j.rootlogger=error, stdout
# sqlmap logging configuration...
log4j.logger.com.ibatis=debug
log4j.logger.com.ibatis.common.jdbc.simpledatasource=debug
log4j.logger.com.ibatis.common.jdbc.scriptrunner=debug
log4j.logger.com.ibatis.sqlmap.engine.impl.sqlmapclientdelegate=debug
log4j.logger.java.sql.connection=debug
log4j.logger.java.sql.statement=debug
log4j.logger.java.sql.preparedstatement=debug
log4j.logger.java.sql.resultset=debug
# console output...
log4j.appender.stdout=org.apache.log4j.consoleappender
log4j.appender.stdout.layout=org.apache.log4j.patternlayout
log4j.appender.stdout.layout.conversionpattern=%5p [%t] - %m%n
分页假设contact 表中有1000条记录并且要在一个电子表格中以每次50条展示给用户。在此情况下,我们不想去查询contact 表得到一个包含1000条记录的resultset 对象;我们要每次查询contact 得到50条记录。sqlmaps 提供paginatedlist 接口来解决像这样的情况。它允许你以一个用户可以前后定位的数据子集来解决。
paginatedlist list = sqlmap.queryforpaginatedlist("getcontacts", null, 2);
while (true) {
iterator listiterator = list.iterator();
while (listiterator.hasnext()) {
system.out.println(
((contact)listiterator.next()).getcontactid());
}
if( list.isnextpageavailable())
list.nextpage();
else
break;
}
结论如果你的应用程序有一个小的固定的查询数目,sqlmaps 是一个非常好的选择。它简单易用,并且可以更有效的利用开发者关于sql的知识。程序员列出所有需要的查询并且开始
java代码方面的工作,把sqlmaps的
xml文件交给
数据库管理员,他会试着分析并且调试查询,sqlmaps还能帮助你完成角色分离。
优点:
1.不依赖于一个or映射
框架所支持的dialects。
2.使用很简单;支持许多高级特性。
3.无需学习像
ejbql这样的新查询语言。允许你从已有的sql知识中获益。
缺点:
1.如果你使用了高级特性,应用可能不能移植。
但如果你的应用拟在多个
数据库上工作,或者有大量的查询,在做出最后的决定之前,你可能需要研究几个or映射
框架。
资源本文的例子代码:[下载文件]
ibatis主页
struts主页
sunil patil有四年的
j2ee工作经验,目前在ibm软件实验室工作。