Spring JDBC 抽象了JDBC API,下表标注X的表示在操作序列中,Spring和開發人員需要關注的操作
操作 | Spring | 開發人員 |
---|---|---|
定義連接配接串 | X | |
Open連接配接 | X | |
指定SQL語句 | X | |
定義參數和參數值 | X | |
prepare和執行語句 | X | |
循環處理結果集 | X | |
每行結果記錄怎麼處理 | X | |
處理異常 | X | |
處理事務 | X | |
關閉連接配接,statement,結果集 | X |
不同的JDBC 通路方式
Spring JDBC 提供了幾種方法,以運用不同類與資料庫的接口。除了JdbcTemplate之外,新的SimpleJdbcinsert 和SimpleJdbcCall兩個類通過利用JDBC驅動提供的資料庫中繼資料來簡化JDBC操作,而RDBMS Object 樣式采用了更類似于JDO Query 設計的面向對象的方法。
- JdbcTemplate
JdbcTemplate是最經典的Spring JDBC 方法。這是一種底層的方法,其他方法内部都借助于JdbcTemplate 來完成。
- NamedParameterJdbcTemplate
NamedParameterJdbcTemplate 封裝了JdbcTemplate以提供命名參數,而不是使用傳統的JDBC "?"占位符。當一個SQL 語句有多個參數時,這種方法則呈現出了更好的可良性和易用性。
- SimpleJdbclnsert 和SimpleJdbcCall
SimpleJdbcinsert 和SimpleJdbcCa ll 優化資料庫中繼資料,以限制必要配置的數量。這種方法簡化了編碼,隻需要提供表或存儲過程的名稱及與列名比對的參數映射。這僅在資料庫提供足夠的中繼資料時有效。如果資料庫不提供此中繼資料, 則必須提供參數的顯式配置。
- RDBMS Object
RDBMS Object 包括MappingSqlQuery 、SqlUpdate 和StoredProcedure ,需要在資料通路層初始化期間建立可重用的且是線程安全的對象。此方法在JDO Query之後模組化,可以在其中定義查詢字元串、聲明參數并編譯查詢。一旦這樣做,執行方法可以多次調用傳人的各種參數值。
JDBC包層次結構
Spring JDBC 由4個不同的包構成,分别為core 、datasource、object和support 。
-
:core
org.springframework.jdbc.core
包包含
JdbcTemplate和大量
callback 接口以及相關類。
org.springframework.jdbc.core.simple
包含
SimpleJdbcInsert
和
SimpleJdbcCall
classes。
org.springframework.jdbc.core.namedparam
包含
NamedParameterJdbcTemplate
以及相關支援類。
-
:datasource
org.springframework.jdbc.datasource
包資料通路的實用工具類和多種DataSource的實作類,可以在Java EE 容器外測試JDBC代碼。
org.springfamework.jdbc.datasource.embedded
子包提供了使用Java 資料庫引擎( 如HSQL 、H2 和Derby )建立嵌入式資料庫的支援
-
:object
org.springframework.jdbc.object
為對象包,以面向對象的方式通路資料庫。它允許執行查詢并傳回結果作為業務對象,可以在資料表的列和業務對象的屬性之間映射查詢結果。.
-
:support
org.springframework.jdbc.support
為支援包,提供了SQLException 轉換功能和一些實用工具類,均是core包和object包的支援類。.
JDBC Core類
JdbcTemplate
JdbcTemplate
JdbcTemplate類是線程安全的。
JdbcTemplate它用于處理資源的建立和釋放,可以避免開發人員常見的JDBC使用錯誤,如忘記關閉連接配接。它執行核心JDBC工作流的基本任務,如語句建立和執行,使應用程式代碼提供SQL并提取結果。JdbcTemplate可以
- 執行Sql查詢
- update語句以及存儲過程調用
- 疊代ResultSet傳回結果。
- 捕獲異常并轉換為DAO異常。
使用JdbcTemplate 時,隻需要實作回調接口即可。PreparedStatementCreator 回調接口根據該類提供的Connection建立一個準備好的語句,提供SQL和任何必需的參數。CallableStatementCreator接口也是如此,該接口建立可調用語句。RowCallbackHandler接口從ResultSet的每一行提取值。
JdbcTemplate可以通過直接執行個體化DataSource引用在DAO實作中使用,或者在Spring IoC容器中配置并作為bean引用提供給DAO 。
DataSource 應始終在Spring IoC 容器中配置為一個bean 。在上述的第一種情況下,bean 直接提供給服務;在第二種情況下,它被提供給準備好的模闆。
查詢(Select)
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
//綁定變量
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
//
String lastName = this.jdbcTemplate.queryForObject( "select last_name from t_actor where id = ?", new Object[]{1212L}, String.class);
//查詢和填充單個域對象,實作RowMapper類
Actor actor = this.jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
new Object[]{1212L},
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
//查詢和填充多個域對象實作RowMapper類
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
Updating ( INSERT
, UPDATE
, and DELETE
)
INSERT
UPDATE
DELETE
//insert
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
//update
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
其他操作
可以使用execute(..)方法來執行任意SQL ,是以該方法通常用于DDL 語句。
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
NamedParameterJdbcTemplate
NamedParameterJdbcTemplate
在經典的JDBC用法中,SQL參數是用占位輪“?”來表示的,這會受到位置的限制。這種方式的問題在于,一旦參數的順序發生變化,就必須改變參數綁定。在Spring JDBC架構中,綁定SQL 參數的另一種選擇是使用命名參數( named parameter ) 。
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
// 基于Map的方式
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
與NamedParameterJdbcTemplate相關的一個很好的功能是SqlParameterSource接口,SqlParameterSource是NamedParameterJdbcTemplate 的命名參數值的來源。
MapSqlParameterSource類是一個非常簡單的實作,它隻是一個圍繞java.util.Map的擴充卡,其中鍵是參數名稱,值是參數值。
另一個SqlParameterSource 實作是BeanPropertySqlParameterSource類。這個類包裝了一個任意的JavaBean ,并使用包裝的JavaBean的屬性作為命名參數值的來源。
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;
}
}
//屬性作為參數源
public int countOfActors(Actor exampleActor) {
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
SQLExceptionTranslator
SQLExceptionTranslator
SQLExceptionTranslator是一個接口,其實作類可以用于SQLExceptions和Spring 自己的org.springframework.dao.DataAccessException 之間進行轉換。
SQLErrorCodeSQLExceptionTranslator是SQLExceptionTranslator的預設實作。該實作使用特定的供應商代碼,它比SQLState實作更精确。錯誤代碼轉換基于JavaBean 類型類SQLErrorCodes中儲存的代碼。該類由SQLErrorCodesFactory 建立和填充,顧名思義,它是一個根據名為sql-error-codes.xml的配置檔案内容建立SQLErrorCodes 的工廠。該檔案使用供應商代碼填充,供應商代碼從DatabaseMetaData擷取DatabaseProductName。
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
// 對錯誤碼:-12345特殊處理
if (sqlex.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, sqlex);
}
return null;
}
}
檢索自動生成的主鍵
update()方法支援檢索由資料庫生成的主鍵。這種支援是JDBC3.0标準的一部分。該方法将PreparedStatementCreator 作為其第一個參數,需要指定插入語句。另一個參數是KeyHolder,它包含從更新成功傳回時生成的主鍵。
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
資料庫連接配接
DataSource
DataSource
Spring通過一個DataSource(資料源)獲得與資料庫的連接配接。在使用Spring的JDBC時,可以從JNDI擷取資料源,或者使用第三方提供的連接配接池實作來配置自己的資料源。流行的實作是Apache Jakarta Commons DBCP 和C3P0 。Spring 發行版本中的實作僅用于測試,并未提供連接配接池方案。是以在使用Spring的DriverManagerDataSource類時,僅用于測試目的,因為它不提供連接配接池的方案,在執行多個連接配接請求時性能較差。
- 代碼方式:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
- Spring方式:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>
<context:property-placeholder location="jdbc.properties"/>
- DBCP
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<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>
<context:property-placeholder location="jdbc.properties"/>
- C3P0
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
更多資訊:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#jdbc