引论
随着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 方法提交你的改动。不考虑你的事务成功与否