﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-176142998-随笔分类-spring</title><link>http://www.blogjava.net/176142998/category/33690.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 02 Apr 2011 00:37:30 GMT</lastBuildDate><pubDate>Sat, 02 Apr 2011 00:37:30 GMT</pubDate><ttl>60</ttl><item><title>开发soap的一个简单的例子</title><link>http://www.blogjava.net/176142998/archive/2011/04/01/347484.html</link><dc:creator>飞飞</dc:creator><author>飞飞</author><pubDate>Fri, 01 Apr 2011 09:21:00 GMT</pubDate><guid>http://www.blogjava.net/176142998/archive/2011/04/01/347484.html</guid><wfw:comment>http://www.blogjava.net/176142998/comments/347484.html</wfw:comment><comments>http://www.blogjava.net/176142998/archive/2011/04/01/347484.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/176142998/comments/commentRss/347484.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/176142998/services/trackbacks/347484.html</trackback:ping><description><![CDATA[<div>1、建一个Project取名为SOAP</div>
<div>2、将axis-1_4\webapps\axis\WEB-INF\lib下的所有文件拷贝到你的SOAP工程文件下</div>
<div>3、新建一个HelloWord.java</div>
<div>
<div>package&nbsp;com;</div>
<div></div>
<div>public&nbsp;class&nbsp;HelloWord&nbsp;{</div>
<div>public&nbsp;String&nbsp;getHelloWord(userInfo&nbsp;userInfo)&nbsp;{</div>
<div>return&nbsp;"hi!:"&nbsp;+&nbsp;userInfo.getName()&nbsp;+&nbsp;"|&nbsp;"&nbsp;+&nbsp;userInfo.getPassword()</div>
<div>+&nbsp;"&nbsp;|&nbsp;"&nbsp;+&nbsp;userInfo.getArea();</div>
<div>}</div>
<div>}</div>
</div>
<div>&nbsp;</div>
<div>4、新建一个userInfo.java 对象</div>
<div>
<div>package&nbsp;com;</div>
<div></div>
<div>public&nbsp;class&nbsp;userInfo&nbsp;implements&nbsp;java.io.Serializable{</div>
<div>/**</div>
<div>&nbsp;*&nbsp;</div>
<div>&nbsp;*/</div>
<div>private&nbsp;static&nbsp;final&nbsp;long&nbsp;serialVersionUID&nbsp;=&nbsp;-1536718814867769008L;</div>
<div>String&nbsp;name;</div>
<div>String&nbsp;password;</div>
<div>String&nbsp;area;</div>
<div>public&nbsp;String&nbsp;getName()&nbsp;{</div>
<div>return&nbsp;name;</div>
<div>}</div>
<div>public&nbsp;void&nbsp;setName(String&nbsp;name)&nbsp;{</div>
<div>this.name&nbsp;=&nbsp;name;</div>
<div>}</div>
<div>public&nbsp;String&nbsp;getPassword()&nbsp;{</div>
<div>return&nbsp;password;</div>
<div>}</div>
<div>public&nbsp;void&nbsp;setPassword(String&nbsp;password)&nbsp;{</div>
<div>this.password&nbsp;=&nbsp;password;</div>
<div>}</div>
<div>public&nbsp;String&nbsp;getArea()&nbsp;{</div>
<div>return&nbsp;area;</div>
<div>}</div>
<div>public&nbsp;void&nbsp;setArea(String&nbsp;area)&nbsp;{</div>
<div>this.area&nbsp;=&nbsp;area;</div>
<div>}</div>
<div></div>
<div>}</div>
</div>
<div>5、在WEB-INF\server-config.wsdd 文件添加以下内容（注意颜色标记的地方时跟soap相关的地方）</div>
<div>
<div>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;</div>
<div>&lt;deployment&nbsp;xmlns="http://xml.apache.org/axis/wsdd/"</div>
<div>xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt;</div>
<div>&lt;handler&nbsp;type="java:org.apache.axis.handlers.http.URLMapper"</div>
<div>name="URLMapper"&nbsp;/&gt;</div>
<div>&lt;service&nbsp;name="HelloWord"&nbsp;provider="java:RPC"&gt;</div>
<div><font style="background-color: #ff00ff">&lt;parameter&nbsp;name="className"&nbsp;value="com.HelloWord"&nbsp;/&gt;</font></div>
<div><font style="background-color: #ff00ff">&lt;parameter&nbsp;name="allowedMethods"&nbsp;value="getHelloWord"&nbsp;/&gt;</font></div>
<div><font style="background-color: #ff00ff">&lt;beanMapping&nbsp;languageSpecificType="java:com.userInfo"&nbsp;qname="ns:userInfo"&nbsp;xmlns:ns="urn:BeanService"/&gt;</font></div>
<div>&lt;/service&gt;</div>
<div>&lt;transport&nbsp;name="http"&gt;</div>
<div>&lt;requestFlow&gt;</div>
<div>&lt;handler&nbsp;type="URLMapper"&nbsp;/&gt;</div>
<div>&lt;/requestFlow&gt;</div>
<div>&lt;/transport&gt;</div>
<div>&lt;/deployment&gt;&nbsp;</div>
<div></div>
</div>
<div>6、web.xml 内容</div>
<div>
<div>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="ISO-8859-1"?&gt;</div>
<div>&lt;!DOCTYPE&nbsp;web-app&nbsp;PUBLIC&nbsp;"-//Sun&nbsp;Microsystems,&nbsp;Inc.//DTD&nbsp;Web</div>
<div>Application&nbsp;2.3//EN"&nbsp;"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</div>
<div>&lt;web-app&gt;</div>
<div>&lt;display-name&gt;Apache-Axis&lt;/display-name&gt;</div>
<div>&lt;servlet&gt;</div>
<div>&lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;</div>
<div>&lt;display-name&gt;Apache-Axis&nbsp;Servlet&lt;/display-name&gt;</div>
<div>&lt;servlet-class&gt;</div>
<div>org.apache.axis.transport.http.AxisServlet</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-class&gt;</div>
<div>&lt;/servlet&gt;</div>
<div>&lt;servlet-mapping&gt;</div>
<div>&lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;</div>
<div>&lt;url-pattern&gt;/servlet/AxisServlet&lt;/url-pattern&gt;</div>
<div>&lt;/servlet-mapping&gt;</div>
<div>&lt;servlet-mapping&gt;</div>
<div>&lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;</div>
<div>&lt;url-pattern&gt;*.jws&lt;/url-pattern&gt;</div>
<div>&lt;/servlet-mapping&gt;</div>
<div>&lt;servlet-mapping&gt;</div>
<div>&lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;</div>
<div>&lt;url-pattern&gt;/services/*&lt;/url-pattern&gt;</div>
<div>&lt;/servlet-mapping&gt;</div>
<div>&lt;welcome-file-list&gt;</div>
<div>&lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;</div>
<div>&lt;welcome-file&gt;index.html&lt;/welcome-file&gt;</div>
<div>&lt;welcome-file&gt;index.jws&lt;/welcome-file&gt;</div>
<div>&lt;/welcome-file-list&gt;</div>
<div>&lt;/web-app&gt;</div>
</div>
<div>7、访问路径 http://localhost:8080/SOAP/services/HelloWord?wsdl </div>
<div>8、使用soapUI&nbsp;3.6.1 生成客户端JUNIT测试代码.进行单元测试！设置soapui的axis参数</div>
<div><img src="file:///C:/Documents%20and%20Settings/Administrator/Local%20Settings/Temporary%20Internet%20Files/Catch7(04-01-17-19-41).jpg"  alt="" /></div>
<div>9、生成客户端JUNIT测试代码</div>
<div><img src="file:///C:/Documents%20and%20Settings/Administrator/Local%20Settings/Temporary%20Internet%20Files/Catch8(04-01-17-19-41).jpg"  alt="" /></div>
<div>&nbsp;</div>
<div><img src="file:///C:/Documents%20and%20Settings/Administrator/Local%20Settings/Temporary%20Internet%20Files/Catch9(04-01-17-19-41).jpg"  alt="" /></div>
<div>&nbsp;</div>
<div>10、将生成的代码放到SOAP工程下。修改测试用例。</div>
<div><img src="file:///C:/Documents%20and%20Settings/Administrator/Local%20Settings/Temporary%20Internet%20Files/Catch12(04-01-17-19-41).jpg"  alt="" /></div>
<div>&nbsp;</div>
<div><img src="file:///C:/Documents%20and%20Settings/Administrator/Local%20Settings/Temporary%20Internet%20Files/Catch10(04-01-17-19-41).jpg"  alt="" /></div>
<div>&nbsp;</div>
<div>11、执行测试，查看结果：</div>
<div><img src="file:///C:/Documents%20and%20Settings/Administrator/Local%20Settings/Temporary%20Internet%20Files/Catch11(04-01-17-19-41).jpg"  alt="" /></div>
<img src ="http://www.blogjava.net/176142998/aggbug/347484.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/176142998/" target="_blank">飞飞</a> 2011-04-01 17:21 <a href="http://www.blogjava.net/176142998/archive/2011/04/01/347484.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring的JDBCTemplate</title><link>http://www.blogjava.net/176142998/archive/2008/08/12/221507.html</link><dc:creator>飞飞</dc:creator><author>飞飞</author><pubDate>Tue, 12 Aug 2008 07:31:00 GMT</pubDate><guid>http://www.blogjava.net/176142998/archive/2008/08/12/221507.html</guid><wfw:comment>http://www.blogjava.net/176142998/comments/221507.html</wfw:comment><comments>http://www.blogjava.net/176142998/archive/2008/08/12/221507.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/176142998/comments/commentRss/221507.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/176142998/services/trackbacks/221507.html</trackback:ping><description><![CDATA[<h1><a name="JdbcTemplate-Spring%E7%9A%84JDBCTemplate"></a>Spring的JDBCTemplate</h1>
<p>当hql等查询方式不能满足性能或灵活性的要求，必须使用SQL时，大家有三种选择：</p>
<p>第一、使用Hibernate 的sql 查询函数，将查询结果对象转为Entity对象。</p>
<p>第二、使用Hibernate Session的getConnection 获得JDBC Connection，然后进行纯JDBC API操作；</p>
<p>第三、选择把Spring的JDBCTemplate作为一种很不错的JDBC Utils来使用。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; JDBCTemplate的使用很简单，只要在ApplicationContext文件里定义一个jdbcTemplate节点，POJO获得注入后可以直接执行操作，不需要继承什么基类，详见<span class="nobr"><a title="Visit page outside Confluence" href="http://www.redsaga.com/spring_ref/2.0/html/jdbc.html" rel="nofollow">JDBCTemplate参考文档<sup><img class="rendericon" height="7" alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width="7" align="absMiddle" border="0" /></sup></a></span>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; AplicationContext定义：</p>
<div class="panel">
<div class="panelContent">
<p>&nbsp;&nbsp;&nbsp; &lt;bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource" ref="dataSource"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
</div>
</div>
<p>实际使用：&nbsp;</p>
<div class="code">
<div class="codeContent">
<pre class="code-java">SqlRowSet rs = jdbcTemplate.queryForRowSet(sql, params);</pre>
</div>
</div>
<p><strong>Tips1：</strong> jdbcTemplate有很多的ORM化回调操作将返回结果转为对象列表，但很多时候还是需要返回ResultSet，Spring有提供一个类似ResultSet的 Spring SqlRowSet对象。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p><strong>Tips2：</strong>.注意jdbcTemplate尽量只执行查询操作，莫要进行更新，否则很容易破坏Hibernate的二级缓存体系。</p>
<!-- <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description rdf:about="http://wiki.springside.org.cn/display/springside/JdbcTemplate" dc:identifier="http://wiki.springside.org.cn/display/springside/JdbcTemplate" dc:title="JdbcTemplate" trackback:ping="http://wiki.springside.org.cn/rpc/trackback/1169" />
</rdf:RDF>
--><!-- Root decorator: all decisions about how a page is to be decorated via the
                    inline decoration begins here.
--><!-- Switch based upon the context. However, for now, just delegate to a decorator
    identified directly by the context.
--><br />
<div class="chapter" lang="en">
<div class="titlepage">
<div>
<div>
<h2 class="title"><a name="jdbc"></a>Chapter&nbsp;11.&nbsp;使用JDBC进行数据访问</h2>
</div>
</div>
<div></div>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both"><a name="jdbc-introduction"></a>11.1.&nbsp;简介</h2>
</div>
</div>
<div></div>
</div>
<p>Spring JDBC抽象框架所带来的价值将在以下几个方面得以体现：（注：使用了Spring JDBC抽象框架之后，应用开发人员只需要完成斜体字部分的编码工作。） </p>
<div class="orderedlist">
<ol type="1">
    <li>
    <p>指定数据库连接参数</p>
    <li>
    <p>打开数据库连接</p>
    <li>
    <p><span class="emphasis"><em>声明SQL语句</em></span></p>
    <li>
    <p>预编译并执行SQL语句</p>
    <li>
    <p>遍历查询结果（如果需要的话）</p>
    <li>
    <p><span class="emphasis"><em>处理每一次遍历操作</em></span></p>
    <li>
    <p>处理抛出的任何异常</p>
    <li>
    <p>处理事务</p>
    <li>
    <p>关闭数据库连接</p>
    </li>
</ol>
</div>
<p>Spring将替我们完成所有单调乏味的JDBC底层细节处理工作。 </p>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="d0e13169"></a>11.1.1.&nbsp;Spring JDBC包结构</h3>
</div>
</div>
<div></div>
</div>
<p>Spring JDBC抽象框架由四个包构成：<tt class="literal">core</tt>、 <tt class="literal">dataSource</tt>、<tt class="literal">object</tt>以及<tt class="literal">support</tt>。 </p>
<p><tt class="literal">org.springframework.jdbc.core</tt>包由<tt class="classname">JdbcTemplate</tt>类以及相关的回调接口（callback interface）和类组成。 </p>
<p><tt class="literal">org.springframework.jdbc.datasource</tt>包由一些用来简化<tt class="interfacename">DataSource</tt>访问的工具类，以及各种<tt class="interfacename">DataSource</tt>接口的简单实现(主要用于单元测试以及在J2EE容器之外使用JDBC)组成。工具类提供了一些静态方法，诸如通过JNDI获取数据连接以及在必要的情况下关闭这些连接。它支持绑定线程的连接，比如被用于<tt class="classname">DataSourceTransactionManager</tt>的连接。 </p>
<p>接下来，<tt class="literal">org.springframework.jdbc.object</tt>包由封装了查询、更新以及存储过程的类组成，这些类的对象都是线程安全并且可重复使用的。它们类似于JDO，与JDO的不同之处在于查询结果与数据库是&#8220;<span class="quote">断开连接</span>&#8221;的。它们是在<tt class="literal">org.springframework.jdbc.core</tt>包的基础上对JDBC更高层次的抽象。 </p>
<p>最后，<tt class="literal">org.springframework.jdbc.support</tt>包提供了一些<tt class="classname">SQLException</tt>的转换类以及相关的工具类。 </p>
<p>在JDBC处理过程中抛出的异常将被转换成<tt class="literal">org.springframework.dao</tt>包中定义的异常。因此使用Spring JDBC进行开发将不需要处理JDBC或者特定的RDBMS才会抛出的异常。所有的异常都是unchecked exception，这样我们就可以对传递到调用者的异常进行有选择的捕获。 </p>
</div>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both"><a name="jdbc-core"></a>11.2.&nbsp;利用JDBC核心类实现JDBC的基本操作和错误处理</h2>
</div>
</div>
<div></div>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-JdbcTemplate"></a>11.2.1.&nbsp;<tt class="classname">JdbcTemplate</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">JdbcTemplate</tt>是core包的核心类。它替我们完成了资源的创建以及释放工作，从而简化了我们对JDBC的使用。它还可以帮助我们避免一些常见的错误，比如忘记关闭数据库连接。JdbcTemplate将完成JDBC核心处理流程，比如SQL语句的创建、执行，而把SQL语句的生成以及查询结果的提取工作留给我们的应用代码。它可以完成SQL查询、更新以及调用存储过程，可以对<tt class="interfacename">ResultSet</tt>进行遍历并加以提取。它还可以捕获JDBC异常并将其转换成<tt class="literal">org.springframework.dao</tt>包中定义的，通用的，信息更丰富的异常。 </p>
<p>使用JdbcTemplate进行编码只需要根据明确定义的一组契约来实现回调接口。<tt class="interfacename">PreparedStatementCreator</tt>回调接口通过给定的<tt class="interfacename">Connection</tt>创建一个PreparedStatement，包含SQL和任何相关的参数。<tt class="literal">CallableStatementCreateor</tt>实现同样的处理，只不过它创建的是CallableStatement。<tt class="literal">RowCallbackHandler</tt>接口则从数据集的每一行中提取值。 </p>
<p>我们可以在一个service实现类中通过传递一个<tt class="interfacename">DataSource</tt>引用来完成JdbcTemplate的实例化，也可以在application context中配置一个JdbcTemplate bean，来供service使用。需要注意的是<tt class="interfacename">DataSource</tt>在application context总是配制成一个bean，第一种情况下，<tt class="interfacename">DataSource</tt> bean将传递给service，第二种情况下<tt class="interfacename">DataSource</tt> bean传递给JdbcTemplate bean。因为JdbcTemplate使用回调接口和<tt class="interfacename">SQLExceptionTranslator</tt>接口作为参数，所以一般情况下没有必要通过继承JdbcTemplate来定义其子类。 </p>
<p>JdbcTemplate中使用的所有SQL将会以&#8220;DEBUG&#8221;级别记入日志（一般情况下日志的category是<tt class="classname">JdbcTemplate</tt>相应的全限定类名，不过如果需要对<tt class="classname">JdbcTemplate</tt>进行定制的话，可能是它的子类名）。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-NamedParameterJdbcTemplate"></a>11.2.2.&nbsp;<tt class="classname">NamedParameterJdbcTemplate</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">NamedParameterJdbcTemplate</tt>类增加了在SQL语句中使用命名参数的支持。在此之前，在传统的SQL语句中，参数都是用<tt class="literal">'?'</tt>占位符来表示的。 <tt class="classname">NamedParameterJdbcTemplate</tt>类内部封装了一个普通的<tt class="classname">JdbcTemplate</tt>，并作为其代理来完成大部分工作。下面的内容主要针对<tt class="classname">NamedParameterJdbcTemplate</tt>与<tt class="classname">JdbcTemplate</tt>的不同之处来加以说明，即如何在SQL语句中使用命名参数。 </p>
<p>通过下面的例子我们可以更好地了解<tt class="classname">NamedParameterJdbcTemplate</tt>的使用模式（在后面我们还有更好的使用方式）。 </p>
<pre class="programlisting"><em class="lineannotation"><span class="lineannotation">// some JDBC-backed DAO class...</span></em>
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(this.getDataSource());
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return template.queryForInt(sql, namedParameters);
}</pre>
<p>在上面例子中，<tt class="literal">sql</tt>变量使用了命名参数占位符&#8220;first_name&#8221;，与其对应的值存在<tt class="literal">namedParameters</tt>变量中（类型为<tt class="classname">MapSqlParameterSource</tt>）。 </p>
<p>如果你喜欢的话，也可以使用基于Map风格的名值对将命名参数传递给<tt class="classname">NamedParameterJdbcTemplate</tt>（<tt class="classname">NamedParameterJdbcTemplate</tt>实现了<tt class="interfacename">NamedParameterJdbcOperations</tt>接口，剩下的工作将由调用该接口的相应方法来完成，这里我们就不再赘述）： </p>
<pre class="programlisting"><em class="lineannotation"><span class="lineannotation">// some JDBC-backed DAO class...</span></em>
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(this.getDataSource());
Map namedParameters = new HashMap();
namedParameters.put("first_name", firstName);
return template.queryForInt(sql, namedParameters);
}</pre>
<p>另外一个值得一提的特性是与<tt class="classname">NamedParameterJdbcTemplate</tt>位于同一个包中的<tt class="interfacename">SqlParameterSource</tt>接口。在前面的代码片断中我们已经看到了该接口的实现（即<tt class="classname">MapSqlParameterSource</tt>类），<tt class="interfacename">SqlParameterSource</tt>可以用来作为<tt class="classname">NamedParameterJdbcTemplate</tt>命名参数的来源。<tt class="classname">MapSqlParameterSource</tt>类是一个非常简单的实现，它仅仅是一个<tt class="interfacename">java.util.Map</tt>适配器，当然其用法也就不言自明了（如果还有不明了的，可以在Spring的JIRA系统中要求提供更多的相关资料）。 </p>
<p><tt class="interfacename">SqlParameterSource</tt>接口的另一个实现－－<tt class="classname">BeanPropertySqlParameterSource</tt>为我们提供了更有趣的功能。该类包装一个类似JavaBean的对象，所需要的命名参数值将由包装对象提供，下面我们使用一个例子来更清楚地说明它的用法。 </p>
<pre class="programlisting"><em class="lineannotation"><span class="lineannotation">// some JavaBean-like class...</span></em>
public class Actor {
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public Long getId() {
return this.id;
}
<em class="lineannotation"><span class="lineannotation">// setters omitted...</span></em>
}</pre>
<pre class="programlisting"><em class="lineannotation"><span class="lineannotation">// some JDBC-backed DAO class...</span></em>
public int countOfActors(Actor exampleActor) {
<em class="lineannotation"><span class="lineannotation">// notice how the named parameters match the properties of the above 'Actor' class</span></em>
String sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName";
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(this.getDataSource());
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return template.queryForInt(sql, namedParameters);
}</pre>
<p>大家必须牢记一点：<tt class="classname">NamedParameterJdbcTemplate</tt>类内部<span class="emphasis"><em>包装</em></span>了一个标准的<tt class="classname">JdbcTemplate</tt>类。如果你需要访问其内部的<tt class="classname">JdbcTemplate</tt>实例（比如访问<tt class="classname">JdbcTemplate</tt>的一些方法）那么你需要使用<tt class="literal">getJdbcOperations()</tt>方法返回的<span class="emphasis"><em><tt class="interfacename">JdbcOperations</tt>接口</em></span>。（<tt class="classname">JdbcTemplate</tt>实现了<tt class="interfacename">JdbcOperations</tt>接口）。 </p>
<p><tt class="classname">NamedParameterJdbcTemplate</tt>类是线程安全的，该类的最佳使用方式不是每次操作的时候实例化一个新的<tt class="classname">NamedParameterJdbcTemplate</tt>，而是针对每个<tt class="interfacename">DataSource</tt>只配置一个<tt class="classname">NamedParameterJdbcTemplate</tt>实例（比如在Spring IoC容器中使用Spring IoC来进行配置），然后在那些使用该类的DAO中共享该实例。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SimpleJdbcTemplate"></a>11.2.3.&nbsp;<tt class="classname">SimpleJdbcTemplate</tt>类</h3>
</div>
</div>
<div></div>
</div>
<div class="note" style="margin-left: 0.5in; margin-right: 0.5in">
<table summary="Note" border="0">
    <tbody>
        <tr>
            <td valign="top" align="center" width="25" rowspan="2"><img alt="[Note]" src="http://www.redsaga.com/spring_ref/2.0/images/admons/note.png" /></td>
            <th align="left">Note</th>
        </tr>
        <tr>
            <td valign="top" align="left" colspan="2">
            <p><span class="emphasis"><em>请注意该类所提供的功能仅适用于Java 5 (Tiger)。</em></span> </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p><tt class="classname">SimpleJdbcTemplate</tt>类是<tt class="classname">JdbcTemplate</tt>类的一个包装器（wrapper），它利用了Java 5的一些语言特性，比如Varargs和Autoboxing。对那些用惯了Java 5的程序员，这些新的语言特性还是很好用的。 </p>
<p><tt class="classname">SimpleJdbcTemplate</tt> 类利用Java 5的语法特性带来的好处可以通过一个例子来说明。在下面的代码片断中我们首先使用标准的<tt class="classname">JdbcTemplate</tt>进行数据访问，接下来使用<tt class="classname">SimpleJdbcTemplate</tt>做同样的事情。 </p>
<pre class="programlisting"><em class="lineannotation"><span class="lineannotation">// classic <tt class="classname">JdbcTemplate</tt>-style...</span></em>
public Actor findActor(long id) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
RowMapper mapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong(Long.valueOf(rs.getLong("id"))));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
<em class="lineannotation"><span class="lineannotation">// normally this would be dependency injected of course...</span></em>
JdbcTemplate jdbcTemplate = new JdbcTemplate(this.getDataSource());
<em class="lineannotation"><span class="lineannotation">// notice the cast, and the wrapping up of the 'id' argument
// in an array, and the boxing of the 'id' argument as a reference type</span></em>
return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
}</pre>
<p>下面是同一方法的另一种实现，惟一不同之处是我们使用了<tt class="classname">SimpleJdbcTemplate</tt>，这样代码显得更加清晰。 </p>
<pre class="programlisting"><em class="lineannotation"><span class="lineannotation">// <tt class="classname">SimpleJdbcTemplate</tt>-style...</span></em>
public Actor findActor(long id) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
ParameterizedRowMapper&lt;Actor&gt; mapper = new ParameterizedRowMapper&lt;Actor&gt;() {
<em class="lineannotation"><span class="lineannotation">// notice the return type with respect to Java 5 covariant return types</span></em>
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
<em class="lineannotation"><span class="lineannotation">// again, normally this would be dependency injected of course...</span></em>
SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(this.getDataSource());
return simpleJdbcTemplate.queryForObject(sql, mapper, id);
}</pre>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="d0e13501"></a>11.2.4.&nbsp;<tt class="interfacename">DataSource</tt>接口</h3>
</div>
</div>
<div></div>
</div>
<p>为了从数据库中取得数据，我们首先需要获取一个数据库连接。 Spring通过<tt class="interfacename">DataSource</tt>对象来完成这个工作。 <tt class="interfacename">DataSource</tt>是JDBC规范的一部分， 它被视为一个通用的数据库连接工厂。通过使用DataSource， Container或Framework可以将连接池以及事务管理的细节从应用代码中分离出来。 作为一个开发人员，在开发和测试产品的过程中，你可能需要知道连接数据库的细节。 但在产品实施时，你不需要知道这些细节。通常数据库管理员会帮你设置好数据源。 </p>
<p>在使用Spring JDBC时，你既可以通过JNDI获得数据源，也可以自行配置数据源（ 使用Spring提供的DataSource实现类）。使用后者可以更方便的脱离Web容器来进行单元测试。 这里我们将使用<tt class="classname">DriverManagerDataSource</tt>，不过DataSource有多种实现， 后面我们会讲到。使用<tt class="classname">DriverManagerDataSource</tt>和你以前获取一个JDBC连接 的做法没什么两样。你首先必须指定JDBC驱动程序的全限定名，这样<tt class="classname">DriverManager</tt> 才能加载JDBC驱动类，接着你必须提供一个url（因JDBC驱动而异，为了保证设置正确请参考相关JDBC驱动的文档）， 最后你必须提供一个用户连接数据库的用户名和密码。下面我们将通过一个例子来说明如何配置一个 <tt class="classname">DriverManagerDataSource</tt>： </p>
<pre class="programlisting">DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");</pre>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SQLExceptionTranslator"></a>11.2.5.&nbsp;<tt class="interfacename">SQLExceptionTranslator</tt>接口</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="interfacename">SQLExceptionTranslator</tt>是一个接口，如果你需要在 <tt class="classname">SQLException</tt>和<tt class="classname">org.springframework.dao.DataAccessException</tt>之间作转换，那么必须实现该接口。 </p>
<p>转换器类的实现可以采用一般通用的做法(比如使用JDBC的SQLState code)，如果为了使转换更准确，也可以进行定制（比如使用Oracle的error code）。 </p>
<p><tt class="classname">SQLErrorCodeSQLExceptionTranslator</tt>是SQLExceptionTranslator的默认实现。 该实现使用指定数据库厂商的error code，比采用<tt class="literal">SQLState</tt>更精确。 转换过程基于一个JavaBean（类型为<tt class="classname">SQLErrorCodes</tt>）中的error code。 这个JavaBean由<tt class="classname">SQLErrorCodesFactory</tt>工厂类创建，其中的内容来自于 "sql-error-codes.xml"配置文件。该文件中的数据库厂商代码基于Database MetaData信息中的 DatabaseProductName，从而配合当前数据库的使用。 </p>
<p>&nbsp;</p>
<p><tt class="classname">SQLErrorCodeSQLExceptionTranslator</tt>使用以下的匹配规则：</p>
<p>&nbsp;</p>
<div class="itemizedlist">
<ul type="disc" compact>
    <li>
    <p>首先检查是否存在完成定制转换的子类实现。通常<tt class="classname">SQLErrorCodeSQLExceptionTranslator</tt> 这个类可以作为一个具体类使用，不需要进行定制，那么这个规则将不适用。 </p>
    <li>
    <p>接着将SQLException的error code与错误代码集中的error code进行匹配。 默认情况下错误代码集将从<tt class="classname">SQLErrorCodesFactory</tt>取得。 错误代码集来自classpath下的sql-error-codes.xml文件， 它们将与数据库metadata信息中的database name进行映射。 </p>
    <li>
    <p>如果仍然无法匹配，最后将调用fallbackTranslator属性的translate方法，<tt class="classname">SQLStateSQLExceptionTranslator</tt>类实例是默认的fallbackTranslator。 </p>
    </li>
</ul>
</div>
<p>&nbsp;</p>
<p><tt class="classname">SQLErrorCodeSQLExceptionTranslator</tt>可以采用下面的方式进行扩展： </p>
<pre class="programlisting">public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
if (sqlex.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, sqlex);
}
return null;
}
}</pre>
<p>在上面的这个例子中，error code为<tt class="literal">'-12345'</tt>的SQLException 将采用该转换器进行转换，而其他的error code将由默认的转换器进行转换。 为了使用该转换器，必须将其作为参数传递给<tt class="classname">JdbcTemplate</tt>类 的<tt class="literal">setExceptionTranslator</tt>方法，并在需要使用这个转换器器的数据 存取操作中使用该<tt class="classname">JdbcTemplate</tt>。 下面的例子演示了如何使用该定制转换器： </p>
<pre class="programlisting">// create a JdbcTemplate and set data source
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
// create a custom translator and set the DataSource for the default translation lookup
MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator();
tr.setDataSource(dataSource);
jt.setExceptionTranslator(tr);
// use the JdbcTemplate for this SqlUpdate
SqlUpdate su = new SqlUpdate();
su.setJdbcTemplate(jt);
su.setSql("update orders set shipping_charge = shipping_charge * 1.05");
su.compile();
su.update();</pre>
<p>在上面的定制转换器中，我们给它注入了一个数据源，因为我们仍然需要 使用默认的转换器从<tt class="literal">sql-error-codes.xml</tt>中获取错误代码集。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="d0e13617"></a>11.2.6.&nbsp;执行SQL语句</h3>
</div>
</div>
<div></div>
</div>
<p>我们仅需要非常少的代码就可以达到执行SQL语句的目的，一旦获得一个 <tt class="interfacename">DataSource</tt>和一个<tt class="classname">JdbcTemplate</tt>， 我们就可以使用<tt class="classname">JdbcTemplate</tt>提供的丰富功能实现我们的操作。 下面的例子使用了极少的代码完成创建一张表的工作。 </p>
<pre class="programlisting">import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAStatement {
private JdbcTemplate jt;
private DataSource dataSource;
public void doExecute() {
jt = new JdbcTemplate(dataSource);
jt.execute("create table mytable (id integer, name varchar(100))");
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}</pre>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="d0e13633"></a>11.2.7.&nbsp;执行查询</h3>
</div>
</div>
<div></div>
</div>
<p>除了execute方法之外，<tt class="classname">JdbcTemplate</tt>还提供了大量的查询方法。 在这些查询方法中，有很大一部分是用来查询单值的。比如返回一个汇总（count）结果 或者从返回行结果中取得指定列的值。这时我们可以使用<tt class="literal">queryForInt(..)</tt>、 <tt class="literal">queryForLong(..)</tt>或者<tt class="literal">queryForObject(..)</tt>方法。 queryForObject方法用来将返回的JDBC类型对象转换成指定的Java对象，如果类型转换失败将抛出 <tt class="classname">InvalidDataAccessApiUsageException</tt>异常。 下面的例子演示了两个查询的用法，一个返回<tt class="literal">int</tt>值，另一个返回 <tt class="classname">String</tt>。 </p>
<pre class="programlisting">import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class RunAQuery {
private JdbcTemplate jt;
private DataSource dataSource;
public int getCount() {
jt = new JdbcTemplate(dataSource);
int count = jt.queryForInt("select count(*) from mytable");
return count;
}
public String getName() {
jt = new JdbcTemplate(dataSource);
String name = (String) jt.queryForObject("select name from mytable", String.class);
return name;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}</pre>
<p>除了返回单值的查询方法，<tt class="classname">JdbcTemplate</tt>还提供了一组返回List结果 的方法。List中的每一项对应查询返回结果中的一行。其中最简单的是<tt class="literal">queryForList</tt>方法， 该方法将返回一个<tt class="interfacename">List</tt>，该<tt class="interfacename">List</tt>中的每一条 记录是一个<tt class="interfacename">Map</tt>对象，对应应数据库中某一行；而该<tt class="interfacename">Map</tt> 中的每一项对应该数据库行中的某一列值。下面的代码片断接着上面的例子演示了如何用该方法返回表中 所有记录： </p>
<pre class="programlisting">public List getList() {
jt = new JdbcTemplate(dataSource);
List rows = jt.queryForList("select * from mytable");
return rows;
}</pre>
<p>返回的结果集类似下面这种形式： </p>
<pre class="programlisting">[{name=Bob, id=1}, {name=Mary, id=2}]</pre>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="d0e13687"></a>11.2.8.&nbsp;更新数据库</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">JdbcTemplate</tt>还提供了一些更新数据库的方法。 在下面的例子中，我们根据给定的主键值对指定的列进行更新。 例子中的SQL语句中使用了&#8220;?&#8221;占位符来接受参数（这种做法在更新和查询SQL语句中很常见）。 传递的参数值位于一个对象数组中（基本类型需要被包装成其对应的对象类型）。 </p>
<pre class="programlisting">import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAnUpdate {
private JdbcTemplate jt;
private DataSource dataSource;
public void setName(int id, String name) {
jt = new JdbcTemplate(dataSource);
jt.update("update mytable set name = ? where id = ?", new Object[] {name, new Integer(id)});
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}</pre>
</div>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both"><a name="jdbc-datasource"></a>11.3.&nbsp;控制数据库连接</h2>
</div>
</div>
<div></div>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-DataSourceUtils"></a>11.3.1.&nbsp;<tt class="classname">DataSourceUtils</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">DataSourceUtils</tt>作为一个帮助类提供易用且强大的数据库访问能力， 我们可以使用该类提供的<tt class="literal">静态</tt>方法从JNDI获取数据库连接以及在必要的时候关闭之。 它提供支持线程绑定的数据库连接（比如使用<tt class="classname">DataSourceTransactionManager</tt> 的时候，将把数据库连接绑定到当前的线程上）。 </p>
<p>注：<tt class="literal">getDataSourceFromJndi(..)</tt>方法主要用于那些没有使用bean factory 或者application context的场合。如果使用application context，那么最好是在 <tt class="classname">JndiObjectFactoryBean</tt>中配置bean或者直接使用 <tt class="classname">JdbcTemplate</tt>实例。<tt class="classname">JndiObjectFactoryBean</tt> 能够通过JNDI获取<tt class="interfacename">DataSource</tt>并将 <tt class="interfacename">DataSource</tt>作为引用参数传递给其他bean。 这样，在不同的<tt class="interfacename">DataSource</tt>之间切换只需要修改配置文件即可， 甚至我们可以用一个非JNDI的<tt class="interfacename">DataSource</tt>来替换 <tt class="interfacename">FactoryBean</tt>定义！ </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SmartDataSource"></a>11.3.2.&nbsp;<tt class="interfacename">SmartDataSource</tt>接口</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="interfacename">SmartDataSource</tt>是<tt class="interfacename">DataSource</tt> 接口的一个扩展，用来提供数据库连接。使用该接口的类在指定的操作之后可以检查是否需要关闭连接。 该接口在某些情况下非常有用，比如有些情况需要重用数据库连接。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-AbstractDataSource"></a>11.3.3.&nbsp;<tt class="classname">AbstractDataSource</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">AbstractDataSource</tt>是一个实现了<tt class="interfacename">DataSource</tt> 接口的<tt class="literal">abstract</tt>基类。它实现了<tt class="interfacename">DataSource</tt>接口的 一些无关痛痒的方法，如果你需要实现自己的<tt class="interfacename">DataSource</tt>，那么继承 该类是个好主意。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SingleConnectionDataSource"></a>11.3.4.&nbsp;<tt class="classname">SingleConnectionDataSource</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">SingleConnectionDataSource</tt>是<tt class="literal">SmartDataSource</tt>接口 的一个实现，其内部包装了一个单连接。该连接在使用之后将不会关闭，很显然它不能在多线程 的环境下使用。 </p>
<p>当客户端代码调用close方法的时候，如果它总是假设数据库连接来自连接池（就像使用持久化工具时一样）， 你应该将<tt class="literal">suppressClose</tt>设置为true。 这样，通过该类获取的将是代理连接（禁止关闭）而不是原有的物理连接。 需要注意的是，我们不能把使用该类获取的数据库连接造型（cast）为Oracle Connection之类的本地数据库连接。 </p>
<p><tt class="classname">SingleConnectionDataSource</tt>主要在测试的时候使用。 它使得测试代码很容易脱离应用服务器而在一个简单的JNDI环境下运行。 与<tt class="classname">DriverManagerDataSource</tt>不同的是，它始终只会使用同一个数据库连接， 从而避免每次建立物理连接的开销。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-DriverManagerDataSource"></a>11.3.5.&nbsp;<tt class="classname">DriverManagerDataSource</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">DriverManagerDataSource</tt>类实现了 <tt class="interfacename">SmartDataSource</tt>接口。在applicationContext.xml中可以使用 bean properties来设置JDBC Driver属性，该类每次返回的都是一个新的连接。 </p>
<p>该类主要在测试以及脱离J2EE容器的独立环境中使用。它既可以用来在application context中作为一个 <tt class="interfacename">DataSource</tt> bean，也可以在简单的JNDI环境下使用。 由于<tt class="literal">Connection.close()</tt>仅仅只是简单的关闭数据库连接，因此任何能够获取 <tt class="interfacename">DataSource</tt>的持久化代码都能很好的工作。不过使用JavaBean风格的连接池 （比如commons-dbcp）也并非难事。即使是在测试环境下，使用连接池也是一种比使用 <tt class="classname">DriverManagerDataSource</tt>更好的做法。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-TransactionAwareDataSourceProxy"></a>11.3.6.&nbsp;<tt class="classname">TransactionAwareDataSourceProxy</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">TransactionAwareDataSourceProxy</tt>作为目标<tt class="interfacename">DataSource</tt>的一个代理， 在对目标<tt class="interfacename">DataSource</tt>包装的同时，还增加了Spring的事务管理能力， 在这一点上，这个类的功能非常像J2EE服务器所提供的事务化的JNDI <tt class="interfacename">DataSource</tt>。 </p>
<div class="note" style="margin-left: 0.5in; margin-right: 0.5in">
<table summary="Note" border="0">
    <tbody>
        <tr>
            <td valign="top" align="center" width="25" rowspan="2"><img alt="[Note]" src="http://www.redsaga.com/spring_ref/2.0/images/admons/note.png" /></td>
            <th align="left">Note</th>
        </tr>
        <tr>
            <td valign="top" align="left" colspan="2">
            <p>该类几乎很少被用到，除非现有代码在被调用的时候需要一个标准的 JDBC <tt class="interfacename">DataSource</tt>接口实现作为参数。 这种情况下，这个类可以使现有代码参与Spring的事务管理。通常最好的做法是使用更高层的抽象 来对数据源进行管理，比如<tt class="classname">JdbcTemplate</tt>和<tt class="classname">DataSourceUtils</tt>等等。 </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p><span class="emphasis"><em>如果需要更详细的资料，请参考<tt class="classname">TransactionAwareDataSourceProxy</tt> JavaDoc 。</em></span> </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-DataSourceTransactionManager"></a>11.3.7.&nbsp;<tt class="classname">DataSourceTransactionManager</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">DataSourceTransactionManager</tt>类是 <tt class="interfacename">PlatformTransactionManager</tt>接口的一个实现，用于处理单JDBC数据源。 它将从指定DataSource取得的JDBC连接绑定到当前线程，因此它也支持了每个数据源对应到一个线程。 </p>
<p>我们推荐在应用代码中使用<tt class="literal">DataSourceUtils.getConnection(DataSource)</tt>来获取 JDBC连接，而不是使用J2EE标准的<tt class="literal">DataSource.getConnection</tt>。因为前者将抛出 unchecked的<tt class="literal">org.springframework.dao</tt>异常，而不是checked的 <tt class="classname">SQLException</tt>异常。Spring Framework中所有的类（比如 <tt class="classname">JdbcTemplate</tt>）都采用这种做法。如果不需要和这个 <tt class="classname">DataSourceTransactionManager</tt>类一起使用，DataSourceUtils 提供的功能跟一般的数据库连接策略没有什么两样，因此它可以在任何场景下使用。 </p>
<p><tt class="classname">DataSourceTransactionManager</tt>类支持定制隔离级别，以及对SQL语句查询超时的设定。 为了支持后者，应用代码必须使用<tt class="classname">JdbcTemplate</tt>或者在每次创建SQL语句时调用 <tt class="literal">DataSourceUtils.applyTransactionTimeout</tt>方法。 </p>
<p>在使用单个数据源的情形下，你可以用<tt class="classname">DataSourceTransactionManager</tt>来替代<tt class="classname">JtaTransactionManager</tt>， 因为<tt class="classname">DataSourceTransactionManager</tt>不需要容器支持JTA。如果你使用<tt class="literal">DataSourceUtils.getConnection(DataSource)</tt>来获取 JDBC连接，二者之间的切换只需要更改一些配置。最后需要注意的一点就是<tt class="classname">JtaTransactionManager</tt>不支持隔离级别的定制！ </p>
</div>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both"><a name="jdbc-object"></a>11.4.&nbsp;用Java对象来表达JDBC操作</h2>
</div>
</div>
<div></div>
</div>
<p><tt class="literal">org.springframework.jdbc.object</tt>包下的类允许用户以更加 面向对象的方式去访问数据库。比如说，用户可以执行查询并返回一个list， 该list作为一个结果集将把从数据库中取出的列数据映射到业务对象的属性上。 用户也可以执行存储过程，以及运行更新、删除以及插入SQL语句。 </p>
<div class="note" style="margin-left: 0.5in; margin-right: 0.5in">
<table summary="Note" border="0">
    <tbody>
        <tr>
            <td valign="top" align="center" width="25" rowspan="2"><img alt="[Note]" src="http://www.redsaga.com/spring_ref/2.0/images/admons/note.png" /></td>
            <th align="left">Note</th>
        </tr>
        <tr>
            <td valign="top" align="left" colspan="2">
            <p>在许多Spring开发人员中间存在有一种观点，那就是下面将要提到的各种RDBMS操作类 （<a title="11.4.4.&nbsp;StoredProcedure类" href="http://www.redsaga.com/spring_ref/2.0/html/jdbc.html#jdbc-StoredProcedure"><tt class="classname">StoredProcedure</tt></a>类除外） 通常也可以直接使用<tt class="classname">JdbcTemplate</tt>相关的方法来替换。 相对于把一个查询操作封装成一个类而言，直接调用<tt class="classname">JdbcTemplate</tt>方法将更简单 而且更容易理解。 </p>
            <p>必须说明的一点就是，这仅仅只是一种<span class="emphasis"><em>观点</em></span>而已， 如果你认为你可以从直接使用RDBMS操作类中获取一些额外的好处， 你不妨根据自己的需要和喜好进行不同的选择。 </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SqlQuery"></a>11.4.1.&nbsp;<tt class="classname">SqlQuery</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">SqlQuery</tt>是一个可重用、线程安全的类，它封装了一个SQL查询。 其子类必须实现<tt class="literal">newResultReader()</tt>方法，该方法用来在遍历 <tt class="interfacename">ResultSet</tt>的时候能使用一个类来保存结果。 我们很少需要直接使用<tt class="classname">SqlQuery</tt>，因为其子类 <tt class="classname">MappingSqlQuery</tt>作为一个更加易用的实现能够将结果集中的行映射为Java对象。 <tt class="classname">SqlQuery</tt>还有另外两个扩展分别是 <tt class="classname">MappingSqlQueryWithParameters</tt>和<tt class="classname">UpdatableSqlQuery</tt>。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-MappingSqlQuery"></a>11.4.2.&nbsp;<tt class="classname">MappingSqlQuery</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">MappingSqlQuery</tt>是一个可重用的查询抽象类，其具体类必须实现 <tt class="literal">mapRow(ResultSet, int)</tt>抽象方法来将结果集中的每一行转换成Java对象。 </p>
<p>在<tt class="interfacename">SqlQuery</tt>的各种实现中， <tt class="classname">MappingSqlQuery</tt>是最常用也是最容易使用的一个。 </p>
<p>下面这个例子演示了一个定制查询，它将从客户表中取得的数据映射到一个 <tt class="classname">Customer</tt>类实例。 </p>
<pre class="programlisting">private class CustomerMappingQuery extends MappingSqlQuery {
public CustomerMappingQuery(DataSource ds) {
super(ds, "SELECT id, name FROM customer WHERE id = ?");
super.declareParameter(new SqlParameter("id", Types.INTEGER));
compile();
}
public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
Customer cust = new Customer();
cust.setId((Integer) rs.getObject("id"));
cust.setName(rs.getString("name"));
return cust;
}
}</pre>
<p>在上面的例子中，我们为用户查询提供了一个构造函数并为构造函数传递了一个 <tt class="interfacename">DataSource</tt>参数。在构造函数里面我们把 <tt class="interfacename">DataSource</tt>和一个用来返回查询结果的SQL语句作为参数 调用父类的构造函数。SQL语句将被用于生成一个<tt class="interfacename">PreparedStatement</tt>对象， 因此它可以包含占位符来传递参数。而每一个SQL语句的参数必须通过调用 <tt class="literal">declareParameter</tt>方法来进行声明，该方法需要一个 <tt class="classname">SqlParameter</tt>（封装了一个字段名字和一个 <tt class="classname">java.sql.Types</tt>中定义的JDBC类型）对象作为参数。 所有参数定义完之后，我们调用<tt class="literal">compile()</tt>方法来对SQL语句进行预编译。 </p>
<p>下面让我们看看该定制查询初始化并执行的代码： </p>
<pre class="programlisting">public Customer getCustomer(Integer id) {
CustomerMappingQuery custQry = new CustomerMappingQuery(dataSource);
Object[] parms = new Object[1];
parms[0] = id;
List customers = custQry.execute(parms);
if (customers.size() &gt; 0) {
return (Customer) customers.get(0);
}
else {
return null;
}
}</pre>
<p>在上面的例子中，getCustomer方法通过传递惟一参数id来返回一个客户对象。 该方法内部在创建<tt class="classname">CustomerMappingQuery</tt>实例之后， 我们创建了一个对象数组用来包含要传递的查询参数。这里我们只有唯一的一个 <tt class="classname">Integer</tt>参数。执行<tt class="classname">CustomerMappingQuery</tt>的 execute方法之后，我们得到了一个<tt class="literal">List</tt>，该List中包含一个 <tt class="classname">Customer</tt>对象，如果有对象满足查询条件的话。 </p>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SqlUpdate"></a>11.4.3.&nbsp;<tt class="classname">SqlUpdate</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">SqlUpdate</tt>类封装了一个可重复使用的SQL更新操作。 跟所有<tt class="classname">RdbmsOperation</tt>类一样，SqlUpdate可以在SQL中定义参数。 </p>
<p>该类提供了一系列<tt class="literal">update()</tt>方法，就像SqlQuery提供的一系列<tt class="literal">execute()</tt>方法一样。 </p>
<p><tt class="classname">SqlUpdate</tt>是一个具体的类。通过在SQL语句中定义参数，这个类可以支持 不同的更新方法，我们一般不需要通过继承来实现定制。 </p>
<pre class="programlisting">import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
public class UpdateCreditRating extends SqlUpdate {
public UpdateCreditRating(DataSource ds) {
setDataSource(ds);
setSql("update customer set credit_rating = ? where id = ?");
declareParameter(new SqlParameter(Types.NUMERIC));
declareParameter(new SqlParameter(Types.NUMERIC));
compile();
}
/**
* @param id for the Customer to be updated
* @param rating the new value for credit rating
* @return number of rows updated
*/
public int run(int id, int rating) {
Object[] params =
new Object[] {
new Integer(rating),
new Integer(id)};
return update(params);
}
}</pre>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-StoredProcedure"></a>11.4.4.&nbsp;<tt class="classname">StoredProcedure</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">StoredProcedure</tt>类是一个抽象基类，它是对RDBMS存储过程的一种抽象。 该类提供了多种<tt class="literal">execute(..)</tt>方法，不过这些方法的访问类型都是<tt class="literal">protected</tt>的。 </p>
<p>从父类继承的<tt class="literal">sql</tt>属性用来指定RDBMS存储过程的名字。 尽管该类提供了许多必须在JDBC3.0下使用的功能，但是我们更关注的是JDBC 3.0中引入的命名参数特性。 </p>
<p>下面的程序演示了如何调用Oracle中的<tt class="literal">sysdate()</tt>函数。 这里我们创建了一个继承<tt class="classname">StoredProcedure</tt>的子类，虽然它没有输入参数， 但是我必须通过使用<tt class="classname">SqlOutParameter</tt>来声明一个日期类型的输出参数。 <tt class="literal">execute()</tt>方法将返回一个map，map中的每个entry是一个用参数名作key， 以输出参数为value的名值对。 </p>
<pre class="programlisting">import java.sql.Types;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.datasource.*;
import org.springframework.jdbc.object.StoredProcedure;
public class TestStoredProcedure {
public static void main(String[] args)  {
TestStoredProcedure t = new TestStoredProcedure();
t.test();
System.out.println("Done!");
}
void test() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("oracle.jdbc.OracleDriver");
ds.setUrl("jdbc:oracle:thin:@localhost:1521:mydb");
ds.setUsername("scott");
ds.setPassword("tiger");
MyStoredProcedure sproc = new MyStoredProcedure(ds);
Map results = sproc.execute();
printMap(results);
}
private class MyStoredProcedure extends StoredProcedure {
private static final String SQL = "sysdate";
public MyStoredProcedure(DataSource ds) {
setDataSource(ds);
setFunction(true);
setSql(SQL);
declareParameter(new SqlOutParameter("date", Types.DATE));
compile();
}
public Map execute() {
<em class="lineannotation"><span class="lineannotation">// the 'sysdate' sproc has no input parameters, so an empty Map is supplied...</span></em>
return execute(new HashMap());
}
}
private static void printMap(Map results) {
for (Iterator it = results.entrySet().iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
}
}</pre>
<p>下面是<tt class="classname">StoredProcedure</tt>的另一个例子，它使用了两个Oracle游标类型的输出参数。 </p>
<pre class="programlisting">import oracle.jdbc.driver.OracleTypes;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class TitlesAndGenresStoredProcedure extends StoredProcedure {
private static final String SPROC_NAME = "AllTitlesAndGenres";
public TitlesAndGenresStoredProcedure(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper()));
compile();
}
public Map execute() {
<em class="lineannotation"><span class="lineannotation">// again, this sproc has no input parameters, so an empty Map is supplied...</span></em>
return super.execute(new HashMap());
}
}</pre>
<p>值得注意的是<tt class="classname">TitlesAndGenresStoredProcedure</tt>构造函数中 <tt class="literal">declareParameter(..)</tt>的<tt class="classname">SqlOutParameter</tt>参数， 该参数使用了<tt class="interfacename">RowMapper</tt>接口的实现。 这是一种非常方便而强大的重用方式。 下面我们来看一下<tt class="interfacename">RowMapper</tt>的两个具体实现。 </p>
<p>首先是<tt class="classname">TitleMapper</tt>类，它简单的把<tt class="interfacename">ResultSet</tt>中的每一行映射为一个<tt class="classname">Title</tt> Domain Object。 </p>
<pre class="programlisting">import com.foo.sprocs.domain.Title;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public final class TitleMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Title title = new Title();
title.setId(rs.getLong("id"));
title.setName(rs.getString("name"));
return title;
}
}</pre>
<p>另一个是<tt class="classname">GenreMapper</tt>类，也是非常简单的将<tt class="interfacename">ResultSet</tt>中的每一行映射为一个<tt class="classname">Genre</tt> Domain Object。 </p>
<pre class="programlisting">import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.foo.domain.Genre;
public final class GenreMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Genre(rs.getString("name"));
}
}</pre>
<p>如果你需要给存储过程传输入参数（这些输入参数是在RDBMS存储过程中定义好了的）， 则需要提供一个指定类型的<tt class="literal">execute(..)</tt>方法， 该方法将调用基类的<tt class="literal">protected</tt> <tt class="literal">execute(Map parameters)</tt>方法。 例如： </p>
<pre class="programlisting">import oracle.jdbc.driver.OracleTypes;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class TitlesAfterDateStoredProcedure extends StoredProcedure {
private static final String SPROC_NAME = "TitlesAfterDate";
private static final String CUTOFF_DATE_PARAM = "cutoffDate";
public TitlesAfterDateStoredProcedure(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declaraParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE);
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
compile();
}
public Map execute(Date cutoffDate) {
Map inputs = new HashMap();
inputs.put(CUTOFF_DATE_PARAM, cutoffDate);
return super.execute(inputs);
}
}</pre>
</div>
<div class="section" lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a name="jdbc-SqlFunction"></a>11.4.5.&nbsp;<tt class="classname">SqlFunction</tt>类</h3>
</div>
</div>
<div></div>
</div>
<p><tt class="classname">SqlFunction</tt> RDBMS操作类封装了一个SQL&#8220;函数&#8221;包装器（wrapper）， 该包装器适用于查询并返回一个单行结果集。默认返回的是一个<tt class="literal">int</tt>值， 不过我们可以采用类似<tt class="classname">JdbcTemplate</tt>中的<tt class="literal">queryForXxx</tt> 做法自己实现来返回其它类型。<tt class="classname">SqlFunction</tt>优势在于我们不必创建 <tt class="classname">JdbcTemplate</tt>，这些它都在内部替我们做了。 </p>
<p>该类的主要用途是调用SQL函数来返回一个单值的结果集，比如类似&#8220;select user()&#8221;、 &#8220;select sysdate from dual&#8221;的查询。如果需要调用更复杂的存储函数， 可以使用<tt class="classname">StoredProcedure</tt>或<tt class="classname">SqlCall</tt>。 </p>
<p><tt class="classname">SqlFunction</tt>是一个具体类，通常我们不需要它的子类。 其用法是创建该类的实例，然后声明SQL语句以及参数就可以调用相关的run方法去多次执行函数。 下面的例子用来返回指定表的记录行数： </p>
<pre class="programlisting">public int countRows() {
SqlFunction sf = new SqlFunction(dataSource, "select count(*) from mytable");
sf.compile();
return sf.run();
}</pre>
</div>
</div>
</div>
<img src ="http://www.blogjava.net/176142998/aggbug/221507.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/176142998/" target="_blank">飞飞</a> 2008-08-12 15:31 <a href="http://www.blogjava.net/176142998/archive/2008/08/12/221507.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring MVC:视图解析器的设置</title><link>http://www.blogjava.net/176142998/archive/2008/08/11/221342.html</link><dc:creator>飞飞</dc:creator><author>飞飞</author><pubDate>Mon, 11 Aug 2008 09:20:00 GMT</pubDate><guid>http://www.blogjava.net/176142998/archive/2008/08/11/221342.html</guid><wfw:comment>http://www.blogjava.net/176142998/comments/221342.html</wfw:comment><comments>http://www.blogjava.net/176142998/archive/2008/08/11/221342.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/176142998/comments/commentRss/221342.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/176142998/services/trackbacks/221342.html</trackback:ping><description><![CDATA[<div>
<p style="font-size: 10pt; margin: 0in"><span style="font-family: 宋体">视图解析器的一些属性</span></p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&lt;bean id="viewResolver"</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; &lt;property name="<span style="color: blue">exposeSpringMacroHelpers</span>" value="true"/&gt;</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; &lt;property name="<span style="color: blue">requestContextAttribute</span>" value="rc"/&gt;</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/&gt;</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; &lt;property name="prefix" value="/"/&gt;</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; &lt;property name="suffix" value=".jsp"/&gt;</p>
<p style="font-size: 10pt; margin: 0in; font-family: 'Courier New'">&lt;/bean&gt;</p>
<p style="font-size: 10pt; margin: 0in"><span style="font-family: 宋体">在视图解析器的定义中，&#8220;</span><span style="color: blue; font-family: 'Courier New'">exposeSpringMacroHelpers</span><span style="font-family: 宋体">&#8221;设置是否通过</span><span style="font-family: 'Courier New'">Spring</span><span style="font-family: 宋体">的宏库暴露一个</span><span style="font-family: 'Courier New'">RequestContext(</span><span style="font-family: 宋体">名为</span><span style="font-family: 'Courier New'">springBindRequestContext)</span><span style="font-family: 宋体">供外部使用，默认值为</span><span style="font-family: 'Courier New'">false</span><span style="font-family: 宋体">。它暴露了处理表单和验证错误信息的宏操作；</span></p>
<p style="font-size: 10pt; margin: 0in"><span style="font-family: 宋体">&#8220;</span><span style="color: blue; font-family: 'Courier New'">requestContextAttribute</span><span style="font-family: 宋体">&#8221;把</span><span style="font-family: 'Courier New'">Spring</span><span style="font-family: 宋体">的</span><span style="font-family: 'Courier New'">RequestContext</span><span style="font-family: 宋体">对象暴露为变量</span><span style="font-family: 'Courier New'">rc</span><span style="font-family: 宋体">。利用</span><span style="font-family: 'Courier New'">${rc.contextPath}</span><span style="font-family: 宋体">来获取应用程序的</span><span style="font-family: 'Courier New'">contextPath(</span><span style="font-family: 宋体">也就是</span><span style="font-family: 'Courier New'">/MyUsers)</span><span style="font-family: 宋体">；利用</span><span style="font-family: 'Courier New'">${rc.getMessage("user.name")}</span><span style="font-family: 宋体">读取</span><span style="font-family: 'Courier New'">/WEB-INF/classes/messages.properties</span><span style="font-family: 宋体">本地化信息。此对象对于那些不访问</span><span style="font-family: 'Courier New'">serlvet</span><span style="font-family: 宋体">请求的</span><span style="font-family: 'Courier New'">View</span><span style="font-family: 宋体">技术</span><span style="font-family: 'Courier New'">(</span><span style="font-family: 宋体">也就是</span><span style="font-family: 'Courier New'">Velocity</span><span style="font-family: 宋体">和</span><span style="font-family: 'Courier New'">FreeMarker</span><span style="font-family: 宋体">模板</span><span style="font-family: 'Courier New'">)</span><span style="font-family: 宋体">来说是必不可少的。</span></p>
<p style="font-size: 10pt; margin: 0in; font-family: 宋体">还有一些属性：</p>
<p style="font-size: 10pt; margin: 0in"><span style="color: blue; font-family: 'Courier New'">exposeRequestAttributes</span><span style="font-family: 宋体">：默认值</span><span style="font-family: 'Courier New'">false</span><span style="font-family: 宋体">，设置是否所有的</span><span style="font-family: 'Courier New'">request</span><span style="font-family: 宋体">属性在与模板进行合并之前添加到</span><span style="font-family: 'Courier New'">model</span><span style="font-family: 宋体">中。（可以理解为</span><span style="font-family: 'Courier New'">request</span><span style="font-family: 宋体">范围内包含的所有对象，而不是一个真正的</span><span style="font-family: 'Courier New'">Request</span><span style="font-family: 宋体">对象。）</span></p>
<p style="font-size: 10pt; margin: 0in"><span style="color: blue; font-family: 'Courier New'">exposeSessionAttributes</span><span style="font-family: 宋体">：默认值</span><span style="font-family: 'Courier New'">false</span><span style="font-family: 宋体">，设置是否所有的</span><span style="font-family: 'Courier New'">session</span><span style="font-family: 宋体">属性在与模板进行合并之前添加到</span><span style="font-family: 'Courier New'">model</span><span style="font-family: 宋体">中。（理解同上）</span></p>
</div>
 <img src ="http://www.blogjava.net/176142998/aggbug/221342.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/176142998/" target="_blank">飞飞</a> 2008-08-11 17:20 <a href="http://www.blogjava.net/176142998/archive/2008/08/11/221342.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>