?
This document uses PHP Chinese website manual Release
JdbcTemplate
是core包的核心類。它替我們完成了資源的創(chuàng)建以及釋放工作,從而簡(jiǎn)化了我們對(duì)JDBC的使用。
它還可以幫助我們避免一些常見的錯(cuò)誤,比如忘記關(guān)閉數(shù)據(jù)庫連接。
JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創(chuàng)建、執(zhí)行,而把SQL語句的生成以及查詢結(jié)果的提取工作留給我們的應(yīng)用代碼。
它可以完成SQL查詢、更新以及調(diào)用存儲(chǔ)過程,可以對(duì)ResultSet
進(jìn)行遍歷并加以提取。
它還可以捕獲JDBC異常并將其轉(zhuǎn)換成org.springframework.dao
包中定義的,通用的,信息更豐富的異常。
使用JdbcTemplate進(jìn)行編碼只需要根據(jù)明確定義的一組契約來實(shí)現(xiàn)回調(diào)接口。
PreparedStatementCreator
回調(diào)接口通過給定的Connection
創(chuàng)建一個(gè)PreparedStatement,包含SQL和任何相關(guān)的參數(shù)。
CallableStatementCreateor
實(shí)現(xiàn)同樣的處理,只不過它創(chuàng)建的是CallableStatement。
RowCallbackHandler
接口則從數(shù)據(jù)集的每一行中提取值。
我們可以在DAO實(shí)現(xiàn)類中通過傳遞一個(gè)DataSource
引用來完成JdbcTemplate的實(shí)例化,也可以在Spring的IoC容器中配置一個(gè)JdbcTemplate的bean并賦予DAO實(shí)現(xiàn)類作為一個(gè)實(shí)例。
需要注意的是DataSource
在Spring的IoC容器中總是配制成一個(gè)bean,第一種情況下,DataSource
bean將傳遞給service,第二種情況下DataSource
bean傳遞給JdbcTemplate bean。
最后,JdbcTemplate中使用的所有SQL將會(huì)以“DEBUG”級(jí)別記入日志(一般情況下日志的category是JdbcTemplate
相應(yīng)的全限定類名,不過如果需要對(duì)JdbcTemplate
進(jìn)行定制的話,可能是它的子類名)。
下面是一些使用JdbcTemplate
類的示例。(這些示例并不是完整展示所有的JdbcTemplate
所暴露出來的功能。請(qǐng)查看與之相關(guān)的Javadoc)。
一個(gè)簡(jiǎn)單的例子用于展示如何獲取一個(gè)表中的所有行數(shù)。
int rowCount = this.jdbcTemplate.queryForInt("select count(0) from t_accrual");
一個(gè)簡(jiǎn)單的例子展示如何進(jìn)行參數(shù)綁定。
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt( "select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
查詢一個(gè)String
。
String surname = (String) this.jdbcTemplate.queryForObject( "select surname from t_actor where id = ?", new Object[]{new Long(1212)}, String.class);
查詢并將結(jié)果記錄為一個(gè)簡(jiǎn)單的數(shù)據(jù)模型。
Actor actor = (Actor) this.jdbcTemplate.queryForObject( "select first_name, surname from t_actor where id = ?", new Object[]{new Long(1212)}, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setSurname(rs.getString("surname")); return actor; } });
查詢并組裝多個(gè)數(shù)據(jù)模型。
Collection actors = this.jdbcTemplate.query( "select first_name, surname from t_actor", new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setSurname(rs.getString("surname")); return actor; } });
如果最后2個(gè)示例中的代碼出現(xiàn)在同一段程序中,我們有必要去掉這些重復(fù)的RowMapper
匿名類代碼,將這些代碼抽取到一個(gè)單獨(dú)的類中(通常是一個(gè)靜態(tài)的內(nèi)部類)。
這樣,這個(gè)內(nèi)部類就可以在DAO的方法中被共享。因而,最后2個(gè)示例寫成如下的形式將更加好:
public Collection findAllActors() { return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper()); } private static final class ActorMapper implements RowMapper { public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setSurname(rs.getString("surname")); return actor; } }
this.jdbcTemplate.update( "insert into t_actor (first_name, surname) values (?, ?)", new Object[] {"Leonor", "Watling"});
this.jdbcTemplate.update( "update t_actor set weapon = ? where id = ?", new Object[] {"Banjo", new Long(5276)});
this.jdbcTemplate.update( "delete from actor where id = ?", new Object[] {new Long.valueOf(actorId)});
execute(..)
方法可以被用作執(zhí)行任何類型的SQL,甚至是DDL語句。
這個(gè)方法的實(shí)現(xiàn)需要傳入一個(gè)回調(diào)接口、需要綁定的參數(shù)數(shù)組等作為參數(shù)。
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
調(diào)用一個(gè)簡(jiǎn)單的存儲(chǔ)過程(更多復(fù)雜的存儲(chǔ)過程支持請(qǐng)參見存儲(chǔ)過程支持)。
this.jdbcTemplate.update( "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", new Object[]{Long.valueOf(unionId)});
JdbcTemplate
類的實(shí)例是線程安全的實(shí)例。這一點(diǎn)非常重要,正因?yàn)槿绱?,你可以配置一個(gè)簡(jiǎn)單的JdbcTemplate
實(shí)例,并將這個(gè)“共享的”、“安全的”實(shí)例注入到不同的DAO類中去。
另外, JdbcTemplate
是有狀態(tài)的,因?yàn)樗S護(hù)的DataSource
實(shí)例是有狀態(tài)的,但是這種狀態(tài)是無法變化的。
使用JdbcTemplate
的一個(gè)常見的最佳實(shí)踐(同時(shí)也是SimpleJdbcTemplate
和NamedParameterJdbcTemplate
類的最佳實(shí)踐)就是在Spring配置文件中配置一個(gè)DataSource
實(shí)例,然后將這個(gè)共享的DataSource
實(shí)例助于到你的DAO中去。
而JdbcTemplate
的實(shí)例將在DataSource
的setter方法中被創(chuàng)建。這樣的話,DAO可能看上去像這樣:
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao
follow...
}
相關(guān)的配置看上去就像這樣。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- the DataSource
(parameterized for configuration via a PropertyPlaceHolderConfigurer
) -->
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
如果你使用Spring提供的JdbcDaoSupport
類,并且你的那些基于JDBC的DAO都繼承自這個(gè)類,那么你會(huì)自動(dòng)地從JdbcDaoSupport
類中繼承了setDataSource(..)
方法。
是否將你的DAO類繼承自這些類完全取決于你自己的決定,事實(shí)上這并不是必須的,如果你看一下JdbcDaoSupport
類你會(huì)發(fā)現(xiàn),這里只是提供了一個(gè)簡(jiǎn)便的方式而已。
無論你是否使用上述這種初始化方式,都無需在執(zhí)行某些SQL操作時(shí)多次創(chuàng)建一個(gè)JdbcTemplate
實(shí)例。記住,一旦JdbcTemplate
被創(chuàng)建,他是一個(gè)線程安全的對(duì)象。
一個(gè)你需要?jiǎng)?chuàng)建多次JdbcTemplate
實(shí)例的理由可能在于,你的應(yīng)用需要訪問多個(gè)不同的數(shù)據(jù)庫,從而需要不同的DataSources
來創(chuàng)建不同的JdbcTemplates
實(shí)例。
NamedParameterJdbcTemplate
類為JDBC操作增加了命名參數(shù)的特性支持,而不是傳統(tǒng)的使用('?'
)作為參數(shù)的占位符。NamedParameterJdbcTemplate
類對(duì)JdbcTemplate
類進(jìn)行了封裝,
在底層,JdbcTemplate
完成了多數(shù)的工作。這一個(gè)章節(jié)將主要描述NamedParameterJdbcTemplate
類與JdbcTemplate
類的一些區(qū)別,也就是使用命名參數(shù)進(jìn)行JDBC操作。
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}
注意這里在'sql'
中使用了命名參數(shù)作為變量,而這個(gè)名稱所對(duì)應(yīng)的值被定義在傳入的'namedParameters'
中作為參數(shù)(也可以傳入到MapSqlParameterSource
中作為參數(shù))。
你也可以傳入許多命名參數(shù)以及他們所對(duì)應(yīng)的值,以Map
的方式,作為鍵值對(duì)傳入到NamedParameterJdbcTemplate
中。
(其余的被NamedParameterJdbcOperations
所暴露的接口以及NamedParameterJdbcTemplate
實(shí)現(xiàn)類遵循了類似的方式,此處不包含相關(guān)內(nèi)容)。
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
Map namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}
NamedParameterJdbcTemplate
類所具備的另外一個(gè)比較好的特性就是可以接收SqlParameterSource
作為傳入?yún)?shù) (這個(gè)類位于相同的包定義中)。
你已經(jīng)在先前的一個(gè)例子中看到了這個(gè)接口的一個(gè)具體實(shí)現(xiàn)類。(
MapSqlParameterSource
類)。而SqlParameterSource
這個(gè)接口對(duì)于NamedParameterJdbcTemplate
類的操作而言是一個(gè)傳入的參數(shù)。MapSqlParameterSource
只是一個(gè)非常簡(jiǎn)單的實(shí)現(xiàn),使用了java.util.Map
作為轉(zhuǎn)接器,
其中,Map中的Key表示參數(shù)名稱,而Map中的Value表示參數(shù)值。
另外一個(gè)SqlParameterSource
的實(shí)現(xiàn)類是BeanPropertySqlParameterSource
。這個(gè)類對(duì)傳統(tǒng)的Java進(jìn)行了封裝(也就是那些符合JavaBean標(biāo)準(zhǔn)的類),
并且使用了JavaBean的屬性作為參數(shù)的名稱和值。
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;
}
// setters omitted...
}
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor
' class
String sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}
注意,NamedParameterJdbcTemplate
類只是封裝了JdbcTemplate
模板;
因而如果你需要訪問相應(yīng)被封裝的JdbcTemplate
類,并訪問一些只有在JdbcTemplate
中擁有的功能,你需要使用getJdbcOperations()
方法來進(jìn)行訪問。
請(qǐng)參照第?11.2.1.2?節(jié) “JdbcTemplate
的最佳實(shí)踐”來獲取一些使用NamedParameterJdbcTemplate
的最佳實(shí)踐。
SimpleJdbcTemplate
所提供的一些特性必須工作在Java 5及以上版本。
SimpleJdbcTemplate
類是對(duì)JdbcTemplate
類進(jìn)行的封裝,從而可以充分利用Java 5所帶來的varargs和autoboxing等特性。
SimpleJdbcTemplate
類完全利用了Java 5語法所帶來的蜜糖效應(yīng)。凡是使用過Java 5的程序員們?nèi)绻獜腏ava 5遷移回之前的JDK版本,無疑會(huì)發(fā)現(xiàn)這些特性所帶來的蜜糖效應(yīng)。
“before and after”示例可以成為SimpleJdbcTemplate
類所帶來的蜜糖效應(yīng)的最佳詮釋。
下面的代碼示例首先展示了使用傳統(tǒng)的JdbcTemplate
進(jìn)行JDBC訪問的代碼,接著是使用SimpleJdbcTemplate
類做同樣的事情。
// classic JdbcTemplate
-style...
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
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("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
// notice the cast, the wrapping up of the 'id' argument
// in an array, and the boxing of the 'id' argument as a reference type
return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
}
下面是同樣的邏輯,使用了SimpleJdbcTemplate
;可以看到代碼“干凈”多了:
// SimpleJdbcTemplate
-style...
private SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public Actor findActor(long id) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
ParameterizedRowMapper<Actor> mapper = new ParameterizedRowMapper<Actor>() {
// notice the return type with respect to Java 5 covariant return types
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;
}
};
return this.simpleJdbcTemplate.queryForObject(sql, mapper, id);
}
請(qǐng)同樣參照第?11.2.1.2?節(jié) “JdbcTemplate
的最佳實(shí)踐”來獲取一些SimpleJdbcTemplate
的最佳實(shí)踐
SimpleJdbcTemplate
只是提供了JdbcTemplate
所提供的功能的子類。
如果你需要使用JdbcTemplate
的方法,而這些方法又沒有在SimpleJdbcTemplate
中定義,你需要調(diào)用getJdbcOperations()
方法
獲取相應(yīng)的方法調(diào)用。JdbcOperations
接口中定義的方法需要在這邊做強(qiáng)制轉(zhuǎn)化才能使用。
為了從數(shù)據(jù)庫中取得數(shù)據(jù),我們首先需要獲取一個(gè)數(shù)據(jù)庫連接。Spring通過DataSource
對(duì)象來完成這個(gè)工作。
DataSource
是JDBC規(guī)范的一部分,它被視為一個(gè)通用的數(shù)據(jù)庫連接工廠。通過使用DataSource,
Container或Framework可以將連接池以及事務(wù)管理的細(xì)節(jié)從應(yīng)用代碼中分離出來。
作為一個(gè)開發(fā)人員,在開發(fā)和測(cè)試產(chǎn)品的過程中,你可能需要知道連接數(shù)據(jù)庫的細(xì)節(jié)。但在產(chǎn)品實(shí)施時(shí),你不需要知道這些細(xì)節(jié)。通常數(shù)據(jù)庫管理員會(huì)幫你設(shè)置好數(shù)據(jù)源。
在使用Spring JDBC時(shí),你既可以通過JNDI獲得數(shù)據(jù)源,也可以自行配置數(shù)據(jù)源(使用Spring提供的DataSource實(shí)現(xiàn)類)。使用后者可以更方便的脫離Web容器來進(jìn)行單元測(cè)試。
這里我們將使用DriverManagerDataSource
,不過DataSource有多種實(shí)現(xiàn),
后面我們會(huì)講到。使用DriverManagerDataSource
和你以前獲取一個(gè)JDBC連接
的做法沒什么兩樣。你首先必須指定JDBC驅(qū)動(dòng)程序的全限定名,這樣DriverManager
才能加載JDBC驅(qū)動(dòng)類,接著你必須提供一個(gè)url(因JDBC驅(qū)動(dòng)而異,為了保證設(shè)置正確請(qǐng)參考相關(guān)JDBC驅(qū)動(dòng)的文檔),
最后你必須提供一個(gè)用戶連接數(shù)據(jù)庫的用戶名和密碼。下面我們將通過一個(gè)例子來說明如何配置一個(gè)DriverManagerDataSource
:
DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setUrl("jdbc:hsqldb:hsql://localhost:"); dataSource.setUsername("sa"); dataSource.setPassword("");
SQLExceptionTranslator
是一個(gè)接口,如果你需要在
SQLException
和org.springframework.dao.DataAccessException
之間作轉(zhuǎn)換,那么必須實(shí)現(xiàn)該接口。
轉(zhuǎn)換器類的實(shí)現(xiàn)可以采用一般通用的做法(比如使用JDBC的SQLState code),如果為了使轉(zhuǎn)換更準(zhǔn)確,也可以進(jìn)行定制(比如使用Oracle的error code)。
SQLErrorCodeSQLExceptionTranslator
是SQLExceptionTranslator的默認(rèn)實(shí)現(xiàn)。
該實(shí)現(xiàn)使用指定數(shù)據(jù)庫廠商的error code,比采用SQLState
更精確。轉(zhuǎn)換過程基于一個(gè)JavaBean(類型為SQLErrorCodes
)中的error code。
這個(gè)JavaBean由SQLErrorCodesFactory
工廠類創(chuàng)建,其中的內(nèi)容來自于
“sql-error-codes.xml”配置文件。該文件中的數(shù)據(jù)庫廠商代碼基于 Database MetaData
信息中的DatabaseProductName,從而配合當(dāng)前數(shù)據(jù)庫的使用。
SQLErrorCodeSQLExceptionTranslator
使用以下的匹配規(guī)則:
首先檢查是否存在完成定制轉(zhuǎn)換的子類實(shí)現(xiàn)。通常SQLErrorCodeSQLExceptionTranslator
這個(gè)類可以作為一個(gè)具體類使用,不需要進(jìn)行定制,那么這個(gè)規(guī)則將不適用。
接著將SQLException的error code與錯(cuò)誤代碼集中的error code進(jìn)行匹配。
默認(rèn)情況下錯(cuò)誤代碼集將從SQLErrorCodesFactory
取得。
錯(cuò)誤代碼集來自classpath下的sql-error-codes.xml文件,它們將與數(shù)據(jù)庫metadata信息中的database name進(jìn)行映射。
使用fallback翻譯器。SQLStateSQLExceptionTranslator
類是缺省的fallback翻譯器。
SQLErrorCodeSQLExceptionTranslator
可以采用下面的方式進(jìn)行擴(kuò)展:
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; } }
在上面的這個(gè)例子中,error code為'-12345'
的SQLException將采用該轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換,而其他的error code將由默認(rèn)的轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換。
為了使用該轉(zhuǎn)換器,必須將其作為參數(shù)傳遞給JdbcTemplate
類的setExceptionTranslator
方法,并在需要使用這個(gè)轉(zhuǎn)換器器的數(shù)據(jù)
存取操作中使用該JdbcTemplate
。下面的例子演示了如何使用該定制轉(zhuǎn)換器:
// create aJdbcTemplate
and set data source JdbcTemplate jt = new JdbcTemplate(); jt.setDataSource(dataSource); // create a custom translator and set theDataSource
for the default translation lookup MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator(); tr.setDataSource(dataSource); jt.setExceptionTranslator(tr); // use theJdbcTemplate
for thisSqlUpdate
SqlUpdate su = new SqlUpdate(); su.setJdbcTemplate(jt); su.setSql("update orders set shipping_charge = shipping_charge * 1.05"); su.compile(); su.update();
在上面的定制轉(zhuǎn)換器中,我們給它注入了一個(gè)數(shù)據(jù)源,因?yàn)槲覀內(nèi)匀恍枰? 使用默認(rèn)的轉(zhuǎn)換器從sql-error-codes.xml
中獲取錯(cuò)誤代碼集。
我們僅需要非常少的代碼就可以達(dá)到執(zhí)行SQL語句的目的,一旦獲得一個(gè)
DataSource
和一個(gè)JdbcTemplate
,
我們就可以使用JdbcTemplate
提供的豐富功能實(shí)現(xiàn)我們的操作。下面的例子使用了極少的代碼完成創(chuàng)建一張表的工作。
import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class ExecuteAStatement { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void doExecute() { this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); } }
除了execute方法之外,JdbcTemplate
還提供了大量的查詢方法。
在這些查詢方法中,有很大一部分是用來查詢單值的。比如返回一個(gè)匯總(count)結(jié)果
或者從返回行結(jié)果中取得指定列的值。這時(shí)我們可以使用queryForInt(..)
、
queryForLong(..)
或者queryForObject(..)
方法。
queryForObject方法用來將返回的JDBC類型對(duì)象轉(zhuǎn)換成指定的Java對(duì)象,如果類型轉(zhuǎn)換失敗將拋出
InvalidDataAccessApiUsageException
異常。
下面的例子演示了兩個(gè)查詢的用法,一個(gè)返回int
值,另一個(gè)返回String
。
import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class RunAQuery { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int getCount() { return this.jdbcTemplate.queryForInt("select count(*) from mytable"); } public String getName() { return (String) this.jdbcTemplate.queryForObject("select name from mytable", String.class); } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } }
除了返回單值的查詢方法,JdbcTemplate
還提供了一組返回List結(jié)果
的方法。List中的每一項(xiàng)對(duì)應(yīng)查詢返回結(jié)果中的一行。其中最簡(jiǎn)單的是queryForList
方法,
該方法將返回一個(gè)List
,該List
中的每一條
記錄是一個(gè)Map
對(duì)象,對(duì)應(yīng)應(yīng)數(shù)據(jù)庫中某一行;而該Map
中的每一項(xiàng)對(duì)應(yīng)該數(shù)據(jù)庫行中的某一列值。下面的代碼片斷接著上面的例子演示了如何用該方法返回表中所有記錄:
private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public List getList() { return this.jdbcTemplate.queryForList("select * from mytable"); }
返回的結(jié)果集類似下面這種形式:
[{name=Bob, id=1}, {name=Mary, id=2}]
JdbcTemplate
還提供了一些更新數(shù)據(jù)庫的方法。
在下面的例子中,我們根據(jù)給定的主鍵值對(duì)指定的列進(jìn)行更新。
例子中的SQL語句中使用了“?”占位符來接受參數(shù)(這種做法在更新和查詢SQL語句中很常見)。
傳遞的參數(shù)值位于一個(gè)對(duì)象數(shù)組中(基本類型需要被包裝成其對(duì)應(yīng)的對(duì)象類型)。
import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class ExecuteAnUpdate { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void setName(int id, String name) { this.jdbcTemplate.update( "update mytable set name = ? where id = ?", new Object[] {name, new Integer(id)}); } }
JdbcTemplate
中有一個(gè)update
方法,可以方便地從數(shù)據(jù)庫中獲取數(shù)據(jù)庫自動(dòng)創(chuàng)建的主鍵。(這是JDBC 3.0的標(biāo)準(zhǔn) - 可以參見13.6節(jié)獲取詳細(xì)信息)。
這個(gè)方法使用了PreparedStatementCreator
接口作為第一個(gè)參數(shù), 可以通過這個(gè)接口的實(shí)現(xiàn)類來定義相應(yīng)的Insert語句。另外一個(gè)參數(shù)是KeyHolder
,
一旦update方法成功,這個(gè)參數(shù)將包含生成的主鍵。這里對(duì)于創(chuàng)建合適的PreparedStatement
并沒有一個(gè)統(tǒng)一的標(biāo)準(zhǔn)。(這也解釋了函數(shù)簽名如此定義的原因)。下面是一個(gè)在Oracle上運(yùn)行良好的示例,它可能在其他平臺(tái)上無法工作:
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps =
connection.prepareStatement(INSERT_SQL, new String[] {"id"});
ps.setString(1, name);
return ps;
}
},
keyHolder);
// keyHolder.getKey() now contains the generated key