網站首頁 編程語言 正文
前言
數據訪問作為 Spring Framework 的特性之一,提供了事務、DAO 支持、JDBC、O/R 映射等能力。針對關系型數據庫的訪問,Spring 提供了一個 spring-jdbc
模塊,JdbcTemplate 是這個模塊的核心類,封裝了復雜的 JDBC 操作。
日常開發中,如果不想引入第三方 ORM 框架或者業務比較簡單,可以將 JdbcTemplate 作為首選。
概述
JdbTemplate 只是一個普通的類,并非是一個完整的 ORM 框架,目的僅僅是消除 JDBC 使用的樣板式代碼。JdbcTemplate 支持 Spring 事務管理,并且會將 SQLException 異常轉換為 DataAccessException。
從命名及實現來看,它的設計有點類似設計模式中的模板方法,不過它是通過回調控制算法中的特定步驟。它將一些 JDBC 操作的通用流程封裝到內部,并將一些必須由用戶提供的步驟封裝為接口,由用戶通過方法參數提供。
從下面的表中可以看出 JDBC 操作過程中,JdbcTemplate 封裝的部分與用戶需要提供的部分。
步驟 | Spring | 用戶 |
---|---|---|
DataSource 配置 | √ | |
獲取 Connection | √ | |
定義 SQL 語句 | √ | |
創建 Statement | √ | |
設置參數 | √ | |
執行 SQL | √ | |
遍歷 ResultSet | √ | |
完成每次迭代 | √ | |
處理異常 | √ | |
處理事務 | √ | |
關閉 Statement、ResultSet、Connection | √ |
實例化
使用 JdbcTemplate,首先需要對其實例化,JdbcTemplate 唯一的依賴是 DataSource
,Spring Framework 環境可以將其聲明為 bean 。
@Configuration
public class JdbcConfig {
@Bean
public DataSource dataSource(){
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(Driver.class);
dataSource.setUsername("root");
dataSource.setPassword("12345678");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(Driver.class);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
}
然后直接注入即可。
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addUser() {
jdbcTemplate.update("insert into user(username, password) values('hkp','123')");
}
}
Spring Boot 環境下可以直接直接引入相關 starter。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
<version>2.2.7.RELEASEversion>
dependency>
然后在 applicaiton.properties
文件中進行數據源配置即可。
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test
spring.datasource.username=root
spring.datasource.password=12345678
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=org.springframework.jdbc.datasource.SimpleDriverDataSource
Spring Boot 會自動配置 JdbcTemplate 為 bean,應用可以直接注入。
方法分類
JdbcTemplate 類定義如下。
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
}
數據庫操作的方法由其實現的接口 JdbcOperations
定義,大概可以分為如下幾類:
- 通用執行:類似
Statement.execute
方法,這類方法可以進行任意 CRUD 操作,支持普通 SQL 和存儲過程,使用重載方法execute
表示。 - 查詢:類似
Statement.executeQuery
方法,用于 select 操作,使用方法query*
表示,包括query
、queryForList
、queryForMap
、queryForObject
、queryForRowSet
。 - 更新:類似
Statement.executeUpdate
方法,用于 insert、update、delete 操作,使用重載方法update
表示。 - 批量更新:類似
Statement.executeBatch
方法,用于批量更新操作,使用重載方法batchUpdate
表示。 - 存儲過程:存儲過程方法底層會調用
CallableStatement.execute
,由方法call
表示。
回調接口
JdbcTemplate 方法較多,用戶不必記住每個方法簽名,使用時在 IDE 中輸入關鍵字 execute
、query
、update
、call
、batch
,通過代碼提示選擇合適的方法即可。
其中 SQL 是用戶必須提供的,參數設置是可選的,如果是查詢操作也可以自定義映射關系,這些自定義的部分由用戶通過回調接口提供。下面是一些可能會用到的回調接口。
Connection 回調
Connection
回調對應的接口是 ConnectionCallback
,這個接口用于通用執行,JdbcTemplate
內部獲取到 Connection
之后就會回調這個接口,由用戶控制 Statement
獲取、SQL 執行、結果處理,JdbcTemplate
會自動處理 Connection
的關閉而無需用戶操作。
使用示例如下:
Integer count = jdbcTemplate.execute(new ConnectionCallback<Integer>() {
@Override
public Integer doInConnection(Connection con) throws SQLException, DataAccessException {
PreparedStatement statement = con.prepareStatement("select count(1) as c from user");
ResultSet resultSet = statement.executeQuery();
int count = resultSet.getInt("c");
resultSet.close();
statement.close();
return count;
}
});
Statement 回調
Connection
回調還需要手動創建 Statement
,如果想省去創建 Statement
的工作可以使用 StatementCallback
接口,這個接口也是用于通用查詢,JdbcTemplate
會自動處理 Statement
的關閉。
示例代碼如下:
Integer count = jdbcTemplate.execute(new StatementCallback<Integer>() {
@Override
public Integer doInStatement(Statement stmt) throws SQLException, DataAccessException {
ResultSet resultSet = stmt.executeQuery("select count(1) as c from user");
int count = resultSet.getInt("c");
resultSet.close();
return count;
}
});
ResultSet 抽取
結果抽取用于將 RestultSet
轉換為所需的類型,JdbcTemplate
中有三個接口。
1. ResultSetExtractor
首先是 ResultSetExtractor
,很明顯,這個接口也是用于查詢,JdbcTemplate
獲取到 ResultSet
后就會回調這個接口。
Integer count = jdbcTemplate.query(sql, new ResultSetExtractor<Integer>() {
@Override
public Integer extractData(ResultSet rs) throws SQLException, DataAccessException {
return rs.getInt("c");
}
});
2. RowCallbackHandler
使用 ResultSetExtractor
還需要對 ResultSet
進行遍歷,如果想省去遍歷的工作,并且不需要返回值可以使用 RowCallbackHandler
,這個接口可以處理每次迭代,示例代碼如下。
String sql = "select count(1) as c from user";
jdbcTemplate.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
int count = rs.getInt("c");
}
});
3. RowMapper
通常情況下,我們查詢還是需要將結果映射為 Java 類的,因此更常用的一個回調接口是 RowMapper
,這個接口可以將每行記錄轉換為一個 Java 對象。示例如下:
String sql = "select username,password from user";
List<User> list = jdbcTemplate.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User().setUsername(rs.getString("username"))
.setPassword(rs.getString("password"));
return user;
}
});
PreparedStatement 回調
PreparedStatement
相關的回調接口在 JdbcTemplate
內部比較多,可以大概做如下劃分。
1. PreparedStatement 創建
自定義 PreparedStatement
創建邏輯的回調接口是 PreparedStatementCreator
,這個接口可用于 execute
、query
、update
方法中。示例代碼如下。
Integer count = jdbcTemplate.query(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement("select count(1) as c from user");
}
}, new ResultSetExtractor<Integer>() {
@Override
public Integer extractData(ResultSet rs) throws SQLException, DataAccessException {
return rs.getInt("c");
}
});
2. PreparedStatement 參數設置JdbcTemplate
中有很多重載方法的參數都支持傳入 SQL 中使用的參數,例如下面的方法。
public interface JdbcOperations {
<T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
int update(String sql, @Nullable Object... args) throws DataAccessException;
}
如果想手動設置參數可以使用 PreparedStatementSetter
回調方法,JdbcTemplate
創建 PreparedStatement
之后就會回調這個接口。示例代碼如下。
String sql = "update user set password = '321' where id = ?";
int count = jdbcTemplate.update(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, 1);
}
});
3. PreparedStatement 回調StatementCallback
回調獲取到的是一個 Statement
對象,如果想使用 PreparedStatement
對象,可以使用 PreparedStatementCallback
回調接口,這個接口用于 execute
方法。示例代碼如下。
int count = jdbcTemplate.execute("update user set password = '321' where id = 1", new PreparedStatementCallback<Integer>() {
@Override
public Integer doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
return ps.executeUpdate();
}
});
4. 批量更新回調
批量更新時有兩種設置參數的方式,一種是通過 JdbcTemplate.batchUpdate
方法參數直接設置 SQL 中的參數值,另一種是通過回調的方式,具體又有兩種。
BatchPreparedStatementSetter
用于批量更新時手動設置參數。
List<User> list = Arrays.asList(new User().setUsername("zhangsan").setPassword("123"),
new User().setUsername("lisi").setPassword("456"));
String sql = "update user set password = ? where username = ?";=
int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, list.get(i).getPassword());
ps.setString(2, list.get(i).getUsername());
}
@Override
public int getBatchSize() {
return list.size();
}
});
如果批量更新的數據量比較大, 可以將其進行拆分,例如 100 條數據,每 10 條做一次批量更新操作,這時可以使用 ParameterizedPreparedStatementSetter
接口設置參數。
List<User> list = Arrays.asList(new User().setUsername("zhangsan").setPassword("123"),
new User().setUsername("lisi").setPassword("456"));
String sql = "update user set password = ? where username = ?";
int[][] counts = jdbcTemplate.batchUpdate(sql, list, 1, new ParameterizedPreparedStatementSetter<User>() {
@Override
public void setValues(PreparedStatement ps, User argument) throws SQLException {
ps.setString(1, argument.getPassword());
ps.setString(2, argument.getUsername());
}
});
CallableStatement 回調
與 PreparedStatement
回調類似,CallableStatement
也有兩個接口分別用戶創建 CallableStatement
和設置 CallableStatement
參數,這兩個回調接口是 CallableStatementCreator
和 CallableStatementCallback
。使用示例如下。
Integer count = jdbcTemplate.execute(new CallableStatementCreator() {
@Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
return con.prepareCall("customFun()");
}
}, new CallableStatementCallback<Integer>() {
@Override
public Integer doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
return cs.getInt("c");
}
});
常用操作
上面介紹了一些回調接口,這些回調接口在大多數場景下使用并不多,只有在極端場景下才會使用,JdbcTemplate 將這些回調接口進一步封裝,例如需要創建 Statement
可以直接在方法參數中指定 SQL、需要設置 SQL 參數值也可以直接通過方法參數傳入,只有映射關系可能需要通過 RowMapper
手動配置。
下面總結一些在某些場景下可能會用到的方法。
查詢
1. 查詢單行單列數據
例如查詢符合某些條件的記錄數量,可以使用如下方法。
<T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException;
2. 查詢單行多列數據
查詢某條記錄,并轉換為 Map ,可以使用如下方法。
Map<String, Object> queryForMap(String sql, @Nullable Object... args) throws DataAccessException;
查詢某條記錄,并轉換為所需類型,可以使用如下方法。
<T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
3. 查詢多行單列數據
<T> List<T> queryForList(String sql, @Nullable Object[] args, Class<T> elementType) throws DataAccessException;
4. 查詢多行多列數據
查詢記錄,并轉換為 Map 可以使用如下方法。
List<Map<String, Object>> queryForList(String sql, @Nullable Object... args) throws DataAccessException;
查詢記錄,并轉換為自定義類型,可以使用如下方法。
<T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
5. 小技巧
由于查詢最為復雜,如果不確定用哪個方法,可以先查找 query*
開頭的方法,然后根據方法返回值類型選擇。
更新
這里的更新包含 insert、update、delete 操作。常用方法如下。
int update(String sql, @Nullable Object... args) throws DataAccessException;
批量更新
單條 SQL,不同參數批量更新,,可以使用如下方法。
int[] batchUpdate(String sql, List<Object[]> batchArgs) throws DataAccessException;
如果數據量過大,可以拆分成多次批量更新,使用如下方法。
<T> int[][] batchUpdate(String sql, Collection<T> batchArgs, int batchSize,
ParameterizedPreparedStatementSetter<T> pss) throws DataAccessException;
支持命名參數的 JdbcTemplate
JdbcTemplate
中使用的 SQL 參數使用 ?
表示,設置參數時需要注意按照參數的順序提供值,如果參數比較多不太方便。
spring-jdbc
模塊還提供了一個 NamedParameterJdbcTemplate
類,支持為參數命名。可以使用 :paramName
、:{paramName}
或者 ¶mName
的形式為 SQL 參數指定名稱。例如:
select * from user where username = :username
NamedParameterJdbcTemplate
底層使用 JdbcTemplate
,使用前面我們提到的 PreparedStatementCreator
回調接口解析 SQL 并設置參數。
使用 NamedParameterJdbcTemplate
時不能將命名參數和 ?
混合使用,可以使用 Map
提供參數值。類定義如下。
public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations {
private final JdbcOperations classicJdbcTemplate;
public NamedParameterJdbcTemplate(DataSource dataSource) {
Assert.notNull(dataSource, "DataSource must not be null");
this.classicJdbcTemplate = new JdbcTemplate(dataSource);
}
}
很明顯,它的設計與 JdbcTemplate
類似,由接口 NamedParameterJdbcOperations
提供 JDBC 操作的方法。部分常用方法如下。
<T> List<T> query(String sql, Map<String, ?> paramMap, RowMapper<T> rowMapper) throws DataAccessException;
int update(String sql, Map<String, ?> paramMap) throws DataAccessException;
原文鏈接:https://blog.csdn.net/zzuhkp/article/details/124742388
相關推薦
- 2022-07-16 搭建一個Electron跨平臺桌面應用程序
- 2023-06-17 C#中Stopwatch的使用及說明_C#教程
- 2022-02-10 Linux環境下安裝docker環境(親測無坑)_docker
- 2022-08-18 Kotlin函數使用示例教程_Android
- 2022-09-27 React報錯map()?is?not?a?function詳析_React
- 2022-10-15 Go?Excelize?API源碼解讀GetSheetViewOptions與SetPageLayo
- 2022-05-27 詳解Python實現字典合并的四種方法_python
- 2023-11-17 深度學習中分布式訓練的現狀及未來
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支