日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Spring JdbcTemplate 快速上手

作者:大鵬cool 更新時間: 2022-05-20 編程語言

前言

數據訪問作為 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* 表示,包括 queryqueryForListqueryForMapqueryForObjectqueryForRowSet
  • 更新:類似 Statement.executeUpdate 方法,用于 insert、update、delete 操作,使用重載方法 update 表示。
  • 批量更新:類似 Statement.executeBatch 方法,用于批量更新操作,使用重載方法 batchUpdate 表示。
  • 存儲過程:存儲過程方法底層會調用 CallableStatement.execute,由方法 call 表示。

回調接口

JdbcTemplate 方法較多,用戶不必記住每個方法簽名,使用時在 IDE 中輸入關鍵字 executequeryupdatecallbatch,通過代碼提示選擇合適的方法即可。

其中 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,這個接口可用于 executequeryupdate 方法中。示例代碼如下。

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 參數,這兩個回調接口是 CallableStatementCreatorCallableStatementCallback。使用示例如下。

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

欄目分類
最近更新