RowMapper:用於將結果集每行數據轉換為需要的類型,用戶需實現方法mapRow(ResultSet rs, int rowNum)來完成將每行數據轉換為相應的類型。
RowCallbackHandler:用於處理ResultSet的每一行結果,用戶需實現方法processRow(ResultSet rs)來完成處理,在該回調方法中無需執行rs.next(),該操作由JdbcTemplate來執行,用戶只需按行獲取數據然后處理即可。
ResultSetExtractor:用於結果集數據提取,用戶需實現方法extractData(ResultSet rs)來處理結果集,用戶必須處理整個結果集;
//1.查詢一行數據並返回int型結果
jdbcTemplate.queryForInt("select count(*) from test");
//2. 查詢一行數據並將該行數據轉換為Map返回
jdbcTemplate.queryForMap("select * from test where name='name5'");
//3.查詢一行任何類型的數據,最后一個參數指定返回結果類型
jdbcTemplate.queryForObject("select count(*) from test", Integer.class);
//4.查詢一批數據,默認將每行數據轉換為Map
jdbcTemplate.queryForList("select * from test");
//5.只查詢一列數據列表,列類型是String類型,列名字是name
jdbcTemplate.queryForList("
select name from test where name=?", new Object[]{"name5"}, String.class);
//6.查詢一批數據,返回為SqlRowSet,類似於ResultSet,但不再綁定到連接上
SqlRowSet rs = jdbcTemplate.queryForRowSet("select * from test");
其中下面這些查詢方法支持多列結果集:
List query(String sql, RowMapper rowMapper)
Map queryForMap(String sql)
Object queryForObject(String sql, RowMapper rowMapper)
List queryForList(String sql)
其他不支持多列結果集的查詢方法則會拋出IncorrectResultSetColumnCountException異常。
從上面的結果可以看出只有返回值類型為List的方法可以接收零到多行結果集而不拋出異常,
所以在使用query方法及queryForXXX方法時需要注意處理EmptyResultDataAccessException和IncorrectResultSizeDataAccessException這兩個異常,
這兩個異常反映出數據庫表中數據可能出現了缺失或冗余。如果返回值不符合期望值時,則需要排查業務流程或者數據了。
最后我們來看看RowMapper接口,這個接口的實現類的功能是將結果集中的每一行數據封裝成用戶定義的結構,所以在查詢方法中經常會被用到。
Spring框架為我們提供了BeanPropertyRowMapper/ParameterizedBeanPropertyRowMapper,ColumnMapRowMapper和SingleColumnRowMapper這三大便利類。
BeanPropertyRowMapper類與ParameterizedBeanPropertyRowMapper類的功能完全相同,當POJO對象和數據庫表字段完全對應或者駝峰式與下划線式對應時,
該類會根據構造函數中傳遞的class來自動填充數據。只是ParameterizedBeanPropertyRowMapper類使用泛型需要JDK5+支持。這里需要注意雖然這兩個類提供了便利,
但是由於使用反射導致性能下降,所以如果需要高性能則還是需要自己去實現RowMapper接口來包裝數據。
ColumnMapRowMapper類返回一個List對象,對象中的每一個元素都是一個以列名為key的Map對象。
SingleColumnRowMapper類也返回一個List對象,對象中的每個元素是數據庫中的某列的值。注意結果集必須是單列,不然會拋出IncorrectResultSetColumnCountException異常。
JdbcTemplate是core包的核心類。它替我們完成了資源的創建以及釋放工作,從而簡化了我們對JDBC的使用。它還可以幫助我們避免一些常見的錯誤,比如忘記關閉數據庫連接。
JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創建、執行,而把SQL語句的生成以及查詢結果的提取工作留給我們的應用代碼。它可以完成SQL查詢、更新以及調用存儲過程,
可以對ResultSet進行遍歷並加以提取。它還可以捕獲JDBC異常並將其轉換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。
使用JdbcTemplate進行編碼只需要根據明確定義的一組契約來實現回調接口。PreparedStatementCreator回調接口通過給定的Connection創建一個PreparedStatement,
包含SQL和任何相關的參數。CallableStatementCreateor實現同樣的處理,只不過它創建的是CallableStatement。RowCallbackHandler接口則從數據集的每一行中提取值。
我們可以在一個service實現類中通過傳遞一個DataSource引用來完成JdbcTemplate的實例化,也可以在application context中配置一個JdbcTemplate bean,
來供service使用。需要注意的是DataSource在application context總是配制成一個bean,第一種情況下,DataSource bean將傳遞給service,
第二種情況下DataSource bean傳遞給JdbcTemplate bean。因為JdbcTemplate使用回調接口和SQLExceptionTranslator接口作為參數,所以一般情況下沒有必要通過繼承JdbcTemplate來定義其子類。
JdbcTemplate中使用的所有SQL將會以“DEBUG”級別記入日志(一般情況下日志的category是JdbcTemplate相應的全限定類名,不過如果需要對JdbcTemplate進行定制的話,可能是它的子類名)。
1. NamedParameterJdbcTemplate類
NamedParameterJdbcTemplate類增加了在SQL語句中使用命名參數的支持。在此之前,在傳統的SQL語句中,參數都是用'?'占位符來表示的。NamedParameterJdbcTemplate類內部封裝了一個普通的JdbcTemplate,
並作為其代理來完成大部分工作。下面的內容主要針對NamedParameterJdbcTemplate與JdbcTemplate的不同之處來加以說明,即如何在SQL語句中使用命名參數。
通過下面的例子我們可以更好地了解NamedParameterJdbcTemplate的使用模式(在后面我們還有更好的使用方式)。
// some JDBC-backed DAO class...
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);
}
在上面例子中,sql變量使用了命名參數占位符“first_name”,與其對應的值存在namedParameters變量中(類型為MapSqlParameterSource)。
如果你喜歡的話,也可以使用基於Map風格的名值對將命名參數傳遞給NamedParameterJdbcTemplate(NamedParameterJdbcTemplate實現了NamedParameterJdbcOperations接口,
剩下的工作將由調用該接口的相應方法來完成,這里我們就不再贅述):
// some JDBC-backed DAO class...
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);
}
另外一個值得一提的特性是與NamedParameterJdbcTemplate位於同一個包中的SqlParameterSource接口。在前面的代碼片斷中我們已經看到了該接口的實現
(即MapSqlParameterSource類),SqlParameterSource可以用來作為NamedParameterJdbcTemplate命名參數的來源。MapSqlParameterSource類是一個非常簡單的實現,
它僅僅是一個java.util.Map適配器,當然其用法也就不言自明了(如果還有不明了的,可以在Spring的JIRA系統中要求提供更多的相關資料)。
SqlParameterSource接口的另一個實現--BeanPropertySqlParameterSource為我們提供了更有趣的功能。該類包裝一個類似JavaBean的對象,所需要的命名參數值將由包裝對象提供,
大家必須牢記一點:NamedParameterJdbcTemplate類內部包裝了一個標准的JdbcTemplate類。如果你需要訪問其內部的JdbcTemplate實例(比如訪問JdbcTemplate的一些方法)
那么你需要使用getJdbcOperations()方法返回的JdbcOperations接口。(JdbcTemplate實現了JdbcOperations接口)。NamedParameterJdbcTemplate類是線程安全的,
該類的最佳使用方式不是每次操作的時候實例化一個新的NamedParameterJdbcTemplate,而是針對每個DataSource只配置一個NamedParameterJdbcTemplate實例
(比如在Spring IoC容器中使用Spring IoC來進行配置),然后在那些使用該類的DAO中共享該實例。
2.SimpleJdbcTemplate類
請注意該類所提供的功能僅適用於Java 5 (Tiger)。
SimpleJdbcTemplate類是JdbcTemplate類的一個包裝器(wrapper),它利用了Java 5的一些語言特性,比如Varargs和Autoboxing。對那些用慣了Java 5的程序員,這些新的語言特性還是很好用的。
SimpleJdbcTemplate 類利用Java 5的語法特性帶來的好處可以通過一個例子來說明。在下面的代碼片斷中我們首先使用標准的JdbcTemplate進行數據訪問,接下來使用SimpleJdbcTemplate做同樣的事情。
// classic JdbcTemplate-style...
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;
}
};
// normally this would be dependency injected of course...
JdbcTemplate jdbcTemplate = new JdbcTemplate(this.getDataSource());
// 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
return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
}
下面是同一方法的另一種實現,惟一不同之處是我們使用了SimpleJdbcTemplate,這樣代碼顯得更加清晰。
// SimpleJdbcTemplate-style...
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;
}
};
// again, normally this would be dependency injected of course...
SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(this.getDataSource());
return simpleJdbcTemplate.queryForObject(sql, mapper, id);
}
11.3. 控制數據庫連接
11.3.1. DataSourceUtils類
DataSourceUtils作為一個幫助類提供易用且強大的數據庫訪問能力, 我們可以使用該類提供的靜態方法從JNDI獲取數據庫連接以及在必要的時候關閉之。
它提供支持線程綁定的數據庫連接(比如使用DataSourceTransactionManager 的時候,將把數據庫連接綁定到當前的線程上)。
注:getDataSourceFromJndi(..)方法主要用於那些沒有使用bean factory 或者application context的場合。如果使用application context,
那么最好是在JndiObjectFactoryBean中配置bean或者直接使用 JdbcTemplate實例。JndiObjectFactoryBean
能夠通過JNDI獲取DataSource並將DataSource作為引用參數傳遞給其他bean。 這樣,在不同的DataSource之間切換只需要修改配置文件即可,
甚至我們可以用一個非JNDI的DataSource來替換FactoryBean定義!
11.3.2. SmartDataSource接口
SmartDataSource是DataSource 接口的一個擴展,用來提供數據庫連接。使用該接口的類在指定的操作之后可以檢查是否需要關閉連接。
該接口在某些情況下非常有用,比如有些情況需要重用數據庫連接。
11.3.3. AbstractDataSource類
AbstractDataSource是一個實現了DataSource 接口的abstract基類。它實現了DataSource接口的 一些無關痛癢的方法,如果你需要實現自己的DataSource,那么繼承該類是個好主意。
11.3.4. SingleConnectionDataSource類
SingleConnectionDataSource是SmartDataSource接口 的一個實現,其內部包裝了一個單連接。該連接在使用之后將不會關閉,很顯然它不能在多線程 的環境下使用。
當客戶端代碼調用close方法的時候,如果它總是假設數據庫連接來自連接池(就像使用持久化工具時一樣), 你應該將suppressClose設置為true。 這樣,通過該類獲取的將是代理連接
(禁止關閉)而不是原有的物理連接。 需要注意的是,我們不能把使用該類獲取的數據庫連接造型(cast)為Oracle Connection之類的本地數據庫連接。
SingleConnectionDataSource主要在測試的時候使用。 它使得測試代碼很容易脫離應用服務器而在一個簡單的JNDI環境下運行。 與DriverManagerDataSource不同的是,
它始終只會使用同一個數據庫連接, 從而避免每次建立物理連接的開銷。
11.3.5. DriverManagerDataSource類
DriverManagerDataSource類實現了SmartDataSource接口。在applicationContext.xml中可以使用 bean properties來設置JDBC Driver屬性,該類每次返回的都是一個新的連接。
該類主要在測試以及脫離J2EE容器的獨立環境中使用。它既可以用來在application context中作為一個DataSource bean,也可以在簡單的JNDI環境下使用。
由於Connection.close()僅僅只是簡單的關閉數據庫連接,因此任何能夠獲取DataSource的持久化代碼都能很好的工作。不過使用JavaBean風格的連接池 (比如commons-dbcp)
也並非難事。即使是在測試環境下,使用連接池也是一種比使用DriverManagerDataSource更好的做法。
11.3.6. TransactionAwareDataSourceProxy類
TransactionAwareDataSourceProxy作為目標DataSource的一個代理, 在對目標DataSource包裝的同時,還增加了Spring的事務管理能力, 在這一點上,這個類的功能非常像J2EE服務器所提供的事務化的JNDIDataSource。
該類幾乎很少被用到,除非現有代碼在被調用的時候需要一個標准的 JDBC DataSource接口實現作為參數。 這種情況下,這個類可以使現有代碼參與Spring的事務管理。
通常最好的做法是使用更高層的抽象 來對數據源進行管理,比如JdbcTemplate和DataSourceUtils等等。
11.3.7. DataSourceTransactionManager類
DataSourceTransactionManager類是PlatformTransactionManager接口的一個實現,用於處理單JDBC數據源。 它將從指定DataSource取得的JDBC連接綁定到當前線程,
因此它也支持了每個數據源對應到一個線程。我們推薦在應用代碼中使用DataSourceUtils.getConnection(DataSource)來獲取 JDBC連接,而不是使用J2EE標准的DataSource.getConnection。
因為前者將拋出 unchecked的org.springframework.dao異常,而不是checked的SQLException異常。Spring Framework中所有的類(比如 JdbcTemplate)都采用這種做法。
如果不需要和這個 DataSourceTransactionManager類一起使用,DataSourceUtils 提供的功能跟一般的數據庫連接策略沒有什么兩樣,因此它可以在任何場景下使用。
DataSourceTransactionManager類支持定制隔離級別,以及對SQL語句查詢超時的設定。 為了支持后者,應用代碼必須使用JdbcTemplate或者在每次創建SQL語句時調用DataSourceUtils.applyTransactionTimeout方法。
在使用單個數據源的情形下,你可以用DataSourceTransactionManager來替代JtaTransactionManager, 因為DataSourceTransactionManager不需要容器支持JTA。
如果你使用DataSourceUtils.getConnection(DataSource)來獲取 JDBC連接,二者之間的切換只需要更改一些配置。最后需要注意的一點就是JtaTransactionManager不支持隔離級別的定制!
用於查詢的回調接口定義主要有以下三種:
org.springframework.jdbc.core.ResultSetExtractor. 基本上屬於JdbcTemplate內部使用的Callback接口,相對於下面兩個Callback接口來說,ResultSetExtractor擁有更多的控制權,
因為使用它,你需要自行處理ResultSet:
public interface ResultSetExtractor
{ Object extractData(ResultSet rs) throws SQLException, DataAccessException; }
在直接處理完ResultSet之后,你可以將處理后的結果以任何你想要的形式包裝后返回。
org.springframework.jdbc.core.RowCallbackHandler. RowCallbackHandler相對於ResultSetExtractor來說,僅僅關注單行結果的處理,
處理后的結果可以根據需要存放到當前RowCallbackHandler對象內或者使用JdbcTemplate的程序上下文中,當然,這個完全是看個人愛好了。 RowCallbackHandler的定義如下:
public interface RowCallbackHandler
{ void processRow(ResultSet rs) throws SQLException; }
org.springframework.jdbc.core.RowMapper. ResultSetExtractor的精簡版,功能類似於RowCallbackHandler,也只關注處理單行的結果,不過,處理后的結果會由ResultSetExtractor實現類進行組合。 RowMapper的接口定義如下:
public interface RowMapper
{ Object mapRow(ResultSet rs, int rowNum) throws SQLException; }
為了說明這三種Callback接口的使用和相互之間的區別,我們暫且設定如下場景:
數據庫表customer中存在多行信息,對該表查詢后,我們需要將每一行的顧客信息都映射到域對象Customer中,並以java.util.List的形式返回所有的查詢結果。
使用三種Callback接口作為參數的query方法的返回值不同:
以ResultSetExtractor作為方法參數的query方法返回Object型結果,要使用查詢結果,我們需要對其進行強制轉型;
以RowMapper接口作為方法參數的query方法直接返回List型的結果;
以RowCallbackHandler作為方法參數的query方法,返回值為void;
使用ResultSetExtractor作為Callback接口處理查詢結果,我們需要自己聲明集合類,自己遍歷ResultSet,自己根據每行數據組裝Customer對象,自己將組裝后的Customer對象添加到集合類中,方法最終只負責將組裝完成的集合返回;
使用RowMapper比直接使用ResultSetExtractor要方便的多,只負責處理單行結果就行,現在,我們只需要將單行的結果組裝后返回就行,剩下的工作,全部都是JdbcTemplate內部的事情了。 實際上,JdbcTemplae內部會使用一個ResultSetExtractor實現類來做其余的工作
JdbcTemplae內部使用的這個ResultSetExtractor實現類為org.springframework.jdbc.core.RowMapperResultSetExtractor, 它內部持有一個RowMapper實例的引用,當處理結果集的時候,會將單行數據的處理委派給其所持有的RowMapper實例,而其余工作它負責:
public Object extractData(ResultSet rs) throws SQLException {
List results = (this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList());
int rowNum = 0;
while (rs.next()) {
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
}
這下應該清楚為啥RowMapper為啥就處理單行結果就能完成ResultSetExtractor頗費周折的工作了吧?!
RowCallbackHandler雖然與RowMapper同是處理單行數據,不過,除了要處理單行結果,它還得負責最終結果的組裝和獲取工作,在這里我們是使用當前上下文聲明的List取得最終查詢結果, 不過,我們也可以單獨聲明一個RowCallbackHandler實現類,
在其中聲明相應的集合類,這樣,我們可以通過該RowCallbackHandler實現類取得最終查詢結果:
public class GenericRowCallbackHandler implements RowCallbackHandler {
private List collections = new ArrayList();
public void processRow(ResultSet rs) throws SQLException {
Customer customer = new Customer();
customer.setFirstName(rs.getString(1));
customer.setLastName(rs.getString(2));
collections.add(customer);
}
public List getResults()
{
return collections;
}
}
GenericRowCallbackHandler handler = new GenericRowCallbackHandler();
jdbcTemplate.query("select * from customer",handler());
List customerList = handler.getResults();
該使用方式是明了了,不過GenericRowCallbackHandler重用性不佳。
RowCallbackHandler因為也是處理單行數據,所以,總得有人來做遍歷ResultSet的工作,這個人其實也是一個ResultSetExtractor實現類, 它是JdbcTemplate一個內部靜態類,名為RowCallbackHandlerResultSetExtractor,一看它的定義你就知道奧秘之所在了:
private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor {
private final RowCallbackHandler rch;
public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
this.rch = rch;
}
public Object extractData(ResultSet rs) throws SQLException {
while (rs.next()) {
this.rch.processRow(rs);
}
return null;
}
}
總的來說,內部工作歸根結底是由ResultSetExtractor做了,RowCallbackHandler和RowMapper只是為了幫助我們簡化使用上的操作而已。 所以,實際使用中,RowCallbackHandler和RowMapper才是我們最常用的選擇。
package jdbc;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import jdbc.model.Person;
import jdbc.service.PersonService;
@ContextConfiguration(locations = "classpath:beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JunitTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
private PersonService personService;
@Test
public void query1() {
List<Person> a = jdbcTemplate.query("select * from person where id = ? or name = ?", new Object[] { 1, "張三" },
new RowMapper<Person>() {
@Override
public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
Person p = new Person();
p.setId(rs.getInt("id"));
p.setName(rs.getString("name"));
p.setAge(rs.getInt("age"));
p.setPass(rs.getString("pass"));
return p;
}
});
System.out.println(a);
Map<String, Object> map = new HashMap<>();
ArrayList<Integer> arr = new ArrayList<>();
arr.add(1);
arr.add(3);
map.put("ids", arr);
List<Person> b = namedParameterJdbcTemplate.query("select * from person where id IN (:ids)", map,
new BeanPropertyRowMapper<>(Person.class));
System.out.println(b);
}
@Test
public void save1() {
Person p = new Person();
p.setId(5);
p.setAge(11);
p.setName("Ead");
p.setPass("00000");
jdbcTemplate.update("insert into person(id,name,age,pass) values(?,?,?,?)",
new Object[] { p.getId(), p.getName(), p.getAge(), p.getPass() });
}
@Test
public void save3() {
Calendar c = Calendar.getInstance();
jdbcTemplate.update("insert into testtime(time) values(?)", new Object[] { c.getTime() });
}
@Test
public void save2() {
Person p = new Person();
p.setId(6);
p.setAge(11);
p.setName("Ead");
p.setPass("00000");
Person p1 = new Person();
p1.setId(6);
p1.setAge(11);
p1.setName("Ead");
p1.setPass("00000");
List<Person> list = new ArrayList<>();
list.add(p);
jdbcTemplate.update("insert into person(name,age,pass) values(?,?,?)", new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, p.getName());
ps.setInt(2, p.getAge());
ps.setString(3, p.getPass());
}
});
System.out.println(personService.getPersonList());
}
@Test
public void update1() {
Person p = new Person();
p.setId(6);
p.setAge(11);
p.setName("Eada");
p.setPass("232323");
jdbcTemplate.update("update person set name=? where id=?", new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, p.getName());
ps.setInt(2, p.getId());
}
});
}
@Test
public void update2() {
Person p = new Person();
p.setId(6);
p.setAge(11);
p.setName("Eada");
p.setPass("232323");
jdbcTemplate.update("update person set name=? where id=?", new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, p.getName());
ps.setInt(2, p.getId());
}
});
}
@Test
public void delete() {
jdbcTemplate.update("delete from person where id = ?", new Object[] { 5 },
new int[] { java.sql.Types.INTEGER });
}
@Test
public void queryForInt1() {
int i = jdbcTemplate.queryForInt("select count(0) from person where name = ?", new Object[] { "zhu" });
System.out.println(i);
}
@Test
public void queryForObject2() {
String p = jdbcTemplate.queryForObject("select name from person where id = 3", String.class);
System.out.println(p);
}
@Test
public void queryForObject3() { //
Person p = (Person) jdbcTemplate.queryForObject("select * from person where id = 3", new RowMapper<Person>() {
@Override
public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
Person user = new Person();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPass(rs.getString("pass"));
user.setAge(rs.getInt("age"));
return user;
}
});
System.out.println(p);
}
@Test
public void queryForList2() {
List<String> list = (List<String>) jdbcTemplate.queryForList("select name from person ", String.class);
System.out.println(list);
}
@Test
public void queryForList3() { // 不行
List<Person> list = (List<Person>) jdbcTemplate.queryForList("select * from person ", Person.class);
System.out.println(list);
}
@Test
public void getPersonInfo() {
List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from person");
System.out.println(list);
}
@Test
public void testBeanPropertyRowMapper() { // 推薦,返回 對象list
Collection<Integer> arr = new ArrayList<>();
arr.add(2);
arr.add(3);
List<Person> result = jdbcTemplate.query("select * from person where id >?", new Object[] { 2 },
new BeanPropertyRowMapper<Person>(Person.class));
System.out.println(result);
}
@Test
public void testColumnMapRowMapper() { // 返回對象map
List<Map<String, Object>> result = jdbcTemplate.query("select * from person", new ColumnMapRowMapper());
System.out.println(result);
}
@Test
public void testSingleColumnRowMapper() { // 返回一列集合
List<String> result1 = jdbcTemplate.query("select name from person", new SingleColumnRowMapper<String>());
System.out.println(result1);
}
@Test
public void list1() { // 查詢結果
List<Person> list = (List<Person>) jdbcTemplate.query("select * from person", new RowMapper<Person>() {
@Override
public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
Person user = new Person();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setPass(rs.getString("pass"));
return user;
}
});
System.out.println(list);
}
@Test
public void batchUpdate() {
List<Person> list = new ArrayList<>();
Person p = new Person();
p.setId(6);
p.setAge(11);
p.setName("Edddddddada");
p.setPass("232323");
list.add(p);
int[] updateCounts = jdbcTemplate.batchUpdate("update person set name = ?, pass = ? where id = ?",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, ((Person) list.get(i)).getName());
ps.setString(2, ((Person) list.get(i)).getPass());
ps.setLong(3, ((Person) list.get(i)).getId());
}
@Override
public int getBatchSize() {
return list.size();
}
});
System.out.println(Arrays.toString(updateCounts));
}
}
package jdbc;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import jdbc.model.Person;
import jdbc.service.PersonService;
@ContextConfiguration(locations = "classpath:beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class NamedParameterJdbcTemplateTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
private PersonService personService;
@Test
public void testInsertSql() {
String insertSql = "insert into person (name,age,pass) values(:name,:age,:pass);";
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name", "Aily");
paramMap.put("age", 22);
paramMap.put("pass", "333");
namedParameterJdbcTemplate.update(insertSql, paramMap);
}
@Test
public void addStu() {
String sql = "insert into person (name,age,pass) values(:name,:age,:pass)";
Person p = new Person();
p.setAge(11);
p.setName("Edddad");
p.setPass("000ddd00");
SqlParameterSource ps = new BeanPropertySqlParameterSource(p);
KeyHolder keyholder = new GeneratedKeyHolder();
namedParameterJdbcTemplate.update(sql, ps, keyholder);
int m = keyholder.getKey().intValue();
}
@Test
public void testupdate() {
String updatesql = "update person set age = :age where id > :id ";
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("id", 4);
paramMap.put("age", 10);
namedParameterJdbcTemplate.update(updatesql, paramMap);
}
@Test
public void testupdate1() {
String updatesql = "update person set age = ? where id > ? ";
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("id", 4);
paramMap.put("age", 20);
jdbcTemplate.batchUpdate(updatesql, new BatchPreparedStatementSetter() {
@Override
public int getBatchSize() {
return paramMap.size();
}
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1, (int) paramMap.get("age"));
ps.setInt(2, (int) paramMap.get("id"));
}
});
}
@Test
public void tes1queryq() {
String selectSql = "select id,name from person where name=:name";
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name", "Ead");
List<Integer> result = new ArrayList<Integer>();
namedParameterJdbcTemplate.query(selectSql, paramMap, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
result.add(rs.getInt("id"));
}
});
System.out.println(result);
}
@Test
public void tes1queryq1() {
String selectSql = "select id,name from person where id IN (:ids)";
Collection<Integer> arr = new ArrayList<>();
arr.add(2);
arr.add(3);
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("ids", arr);
List<Integer> result = new ArrayList<Integer>();
namedParameterJdbcTemplate.query(selectSql, paramMap, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
result.add(rs.getInt("id"));
}
});
System.out.println(result);
}
@Test
public void testquery2() {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("id", 4);
String sql = "select name,age,pass from person " + "where id > :id";
List<Map<String, Object>> result = namedParameterJdbcTemplate.queryForList(sql, paramMap);
for (Map<String, Object> map : result) {
System.out.println("------------");
System.out.println(map.get("name"));
System.out.println(map.get("age"));
System.out.println(map.get("pass"));
}
System.out.println(result);
}
@Test
public void testdeleteSql() {
String deleteSql = "delete from test where name=:name";
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name", "Ead");
namedParameterJdbcTemplate.update(deleteSql, paramMap);
}
@Test
public void testBeanPropertyRowMapper() { // 推薦,返回 對象list
List<Person> result = namedParameterJdbcTemplate.query("select * from person",
new BeanPropertyRowMapper<Person>(Person.class));
System.out.println(result);
}
/*-------------------三種結果集的查詢使用------------*/
@Test
public void testResultSetExtractor() {
List<Person> list = (List<Person>) jdbcTemplate.query("select * from person",
new ResultSetExtractor<List<Person>>() {
public List<Person> extractData(ResultSet rs) throws SQLException, DataAccessException {
List<Person> ps = new ArrayList<>();
while (rs.next()) {
Person p = new Person();
p.setId(rs.getInt("id"));
p.setName(rs.getString("name"));
p.setAge(rs.getInt("age"));
p.setPass(rs.getString("pass"));
ps.add(p);
}
return ps;
}
});
System.out.println(list);// 返回整個查詢結果
}
@Test
public void testRowMapper() {
List<Person> list = jdbcTemplate.query("select * from person", new RowMapper<Person>() {
public Person mapRow(ResultSet rs, int rowNumber) throws SQLException {
Person p = new Person();
p.setId(rs.getInt("id"));
p.setName(rs.getString("name"));
p.setAge(rs.getInt("age"));
p.setPass(rs.getString("pass"));
return p;
}
});
System.out.println(list);// 返回整個查詢結果
}
@Test
public void testRowCallbackHandler() {
List<Person> list = new ArrayList<>();
jdbcTemplate.query("select * from person", new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
Person p = new Person();
p.setId(rs.getInt("id"));
p.setName(rs.getString("name"));
p.setAge(rs.getInt("age"));
p.setPass(rs.getString("pass"));
list.add(p);
}
});
System.out.println(list);// 返回整個查詢結果
}
}
