1. 程式人生 > >Spring原始碼之JdbcTemplate分析

Spring原始碼之JdbcTemplate分析

JdbcTemplate

用過Spring開發的,ORM框架一般選擇MyBatis或者Hibernate,不過,Spring對JDBC API的封裝工具JdbcTemplate,也提供了很方便的操作,不需要再在使用jdbc api時捕獲那麼多受檢異常,忍受那麼多樣板式的程式碼了。

JdbcTemplate主要提供以下幾類方法:

  1. execute方法:用於執行任何SQL語句,一般用於執行DDL語句;
  2. update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate批量執行修改;
  3. query方法及queryForXXX方法:用於執行查詢相關語句,query方法返回一個列表,queryForXXX方法一般只返回一個物件;
  4. call方法:用於執行儲存過程、函式相關語句,一般使用不到

JdbcTemplate類支援設定自定義的回撥,比如回撥產生預編譯語句和儲存過程。

如PreparedStatementCreator,使用PreparedStatementCreator,我們可以通過回撥獲取到JdbcTemplate提供的資料庫連線Connection,然後再利用該Conncetion建立自定義的預編譯語句PreparedStatement,如,

// 預編譯語句及儲存過程建立回撥、自定義功能回撥使用:
@Test
public void testPreparedStatement1() {
    int count = jdbcTemplate.execute(new
PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { return conn.prepareStatement("select count(*) from test"); }}, new PreparedStatementCallback<Integer>() { @Override public Integer doInPreparedStatement
(PreparedStatement pstmt) throws SQLException, DataAccessException { pstmt.execute(); ResultSet rs = pstmt.getResultSet(); rs.next(); return rs.getInt(1); }}); Assert.assertEquals(0, count); } // 首先使用PreparedStatementCreator建立一個預編譯語句,其次由JdbcTemplate通過PreparedStatementCallback回撥傳回,由使用者決定如何執行該PreparedStatement。此處我們使用的是execute方法。

還有CallableStatementCreator,使用CallableStatementCreator,我們可以通過回撥獲取到JdbcTemplate提供的資料庫連線Connection,然後再利用該Conncetion建立自定義的CallableStatement。

還有PreparedStatementSetter,使用PreparedStatementSetter,我們可以通過回撥獲取到Spring建立的預編譯語句PreparedStatement,然後再對該PreparedStatement的佔位符引數’?’設定相應的值。如,

// 預編譯語句設值回撥使用:
@Test
public void testPreparedStatement2() {
    String insertSql = "insert into test(name) values (?)";
    int count = jdbcTemplate.update(insertSql, new PreparedStatementSetter() {
        @Override
        public void setValues(PreparedStatement pstmt) throws SQLException {
            pstmt.setObject(1, "name4");
    }});
    Assert.assertEquals(1, count);
    String deleteSql = "delete from test where name=?";
    count = jdbcTemplate.update(deleteSql, new Object[] {"name4"});
    Assert.assertEquals(1, count);
}
// 通過JdbcTemplate的int update(String sql, PreparedStatementSetter pss)執行預編譯sql,其中sql引數為“insert into test(name) values (?) ”,該sql有一個佔位符需要在執行前設值,PreparedStatementSetter實現就是為了設值,使用setValues(PreparedStatement pstmt)回撥方法設值相應的佔位符位置的值。JdbcTemplate也提供一種更簡單的方式“update(String sql, Object... args)”來實現設值,所以只要當使用該種方式不滿足需求時才應使用PreparedStatementSetter。

還有BatchPreparedStatementSetter,類似於上面的PreparedStatementSetter,但用於批量設定和批量執行,而且指定批量的量大小size。

還有自定義功能回撥,可以提供一個擴充套件點,在指定型別的擴充套件點可以執行任何數量需要的操作,如,
ConnectionCallback,使用ConnectionCallback,我們可以通過回撥獲取到JdbcTemplate提供的資料庫連線Connection,然後再利用該Conncetion執行任何數量的操作;

StatementCallback,使用StatementCallback,我們可以通過回撥獲取到JdbcTemplate提供的Statement語句,然後再利用該Statement執行任何數量的操作;

PreparedStatementCallback,使用PreparedStatementCallback,我們可以通過回撥獲取到JdbcTemplate提供的預編譯語句PreparedStatement,然後再利用該PreparedStatement執行任何數量的操作;

CallableStatementCallback,使用CallableStatementCallback,我們可以通過回撥獲取到JdbcTemplate提供的回撥語句CallableStatement,然後再利用該CallableStatement執行任何數量的操作。

對於JdbcTemplate查詢出來的資料,如果要對映成Java物件,一般需要傳遞一個RowMapper實現,RowMapper用於將結果集的每行資料轉換為需要的型別,需要實現方法mapRow(ResultSet rs, int rowNum)來完成轉換。如,

// 結果集處理回撥:
@Test
public void testResultSet1() {
    jdbcTemplate.update("insert into test(name) values('name5')");
    String listSql = "select * from test";
    List result = jdbcTemplate.query(listSql, new RowMapper<Map>() {
        @Override
        public Map mapRow(ResultSet rs, int rowNum) throws SQLException {
            Map row = new HashMap();
            row.put(rs.getInt("id"), rs.getString("name"));
            return row;
    }});
    Assert.assertEquals(1, result.size());
    jdbcTemplate.update("delete from test where name='name5'");
}
// RowMapper介面提供mapRow(ResultSet rs, int rowNum)方法將結果集的每一行轉換為一個Map,當然可以轉換為其他類,如表的物件畫形式。

BeanPropertyRowMapper

對於RowMapper,使用最多的實現是BeanPropertyRowMapper,由名字可以看出,BeanPropertyRowMapper用於匹配Java類屬性和資料庫表字段,其具體實現是,內部存放一個MappedFields的HashMap,key是屬性名稱,value是Java類的屬性。

首先,類的屬性(該屬性要有write方法)名稱全部轉化成小寫,放進map中。

其次,只要類屬性名稱中有大寫字母,把所有這樣的大寫字母都轉化成字串和小寫的形式:“_小寫”,把轉化後的整個屬性名稱放進map中。

注意(BeanPropertyRowMapper中的坑)

BeanPropertyRowMapper預設資料庫欄位名和Python變數名命名類似,以下劃線分割單詞;
預設Java類的屬性名是以camelCase形式或者是下劃線形式(這兩種中的任一種都可以)。

如果資料庫欄位名不使用下劃線分割,則應全部使用小寫。這個時候,Java類屬性名也應全部使用小寫,不能使用camelCase,如資料庫欄位名為companyaddress,那麼Java類該屬性對應的名字也應該為companyaddress,不能為companyAddress。

如,BeanPropertyRowMapper在建立的時候會呼叫initialize方法來初始化,這個時候,就會把Java類的屬性的camelCase風格的名稱轉化成下劃線分割的名稱和全小寫的名稱,如,

這裡寫圖片描述

除了RowMapper,還有RowCallbackHandler,用於處理結果集中的每一行,需要實現processRow(ResultSet rs)方法。注意,在processRow方法中不要執行rs.next(),這個操作由JdbcTemplate來做,我們只需要獲取資料就行。

@Test
public void testResultSet2() {
    jdbcTemplate.update("insert into test(name) values('name5')");
    String listSql = "select * from test";
    final List result = new ArrayList();
    jdbcTemplate.query(listSql, new RowCallbackHandler() {
        @Override
        public void processRow(ResultSet rs) throws SQLException {
            Map row = new HashMap();
            row.put(rs.getInt("id"), rs.getString("name"));
            result.add(row);
    }});
    Assert.assertEquals(1, result.size());
    jdbcTemplate.update("delete from test where name='name5'");
}
// RowCallbackHandler介面也提供方法processRow(ResultSet rs),能將結果集的行轉換為需要的形式。

還有ResultSetExtractor,用於提取ResultSet資料,需要實現extractData(ResultSet rs)方法。注意,在extractData方法中需要我們處理整個ResultSet結果集資料。

@Test
public void testResultSet3() {
    jdbcTemplate.update("insert into test(name) values('name5')");
    String listSql = "select * from test";
    List result = jdbcTemplate.query(listSql, new ResultSetExtractor<List>() {
        @Override
        public List extractData(ResultSet rs)
        throws SQLException, DataAccessException {
            List result = new ArrayList();
            while(rs.next()) {
                Map row = new HashMap();
                row.put(rs.getInt("id"), rs.getString("name"));
                result.add(row);
            }
            return result;
    }});
    Assert.assertEquals(0, result.size());
    jdbcTemplate.update("delete from test where name='name5'");
}
// ResultSetExtractor使用回撥方法extractData(ResultSet rs)提供給使用者整個結果集,讓使用者決定如何處理該結果集。

喜歡的可以關注微信公眾號:

這裡寫圖片描述

更多文章

相關推薦

Spring原始碼JdbcTemplate分析

JdbcTemplate 用過Spring開發的,ORM框架一般選擇MyBatis或者Hibernate,不過,Spring對JDBC API的封裝工具JdbcTemplate,也提供了很方便的操作,不需要再在使用jdbc api時捕獲那麼多受檢異常,忍受那麼

Spring原始碼JdbcTemplate中的坑

我們平常用JdbcTemplate最多的還是query()方法和queryForObject()方法。同樣,其中還有一個使用最多的是BeanPropertyRowMapper。 但是,在JdbcTemplate.queryForObject()中有一個很不起眼的坑,BeanPropertyRow

Spring原始碼設計模式

一.代理模式 分為jdk代理和cglib代理。(前者實現介面,後者生成繼承) 1.兩個參與角色:執行者和被代理人 2.對於被代理人來說,這件事情是一定要完成的,但是自己又不想做或者沒有時間做,找代理。 3.需要獲取到被代理人的資料。 歸納:辦事要求人,所以找代理。 二.工廠模式 分為簡單工廠

Spring BootJdbcTemplate多資料來源配置與使用

之前在介紹使用JdbcTemplate和Spring-data-jpa時,都使用了單資料來源。在單資料來源的情況下,Spring Boot的配置非常簡單,只需要在application.properties檔案中配置連線引數即可。但是往往隨著業務量發展,我們通常會進行資料庫拆分或是引入其他資料庫,從而我們需要

spring原始碼初嘗試

從啟動類的run()方法開始debug,一步一步step into開啟spring原始碼之旅,目前還只能看個大概,慢慢來吧,得多咀嚼,細品味。 原始碼中太多的回撥、錯綜複雜的繼承、實現、反射等思想,一口氣想吞下恐怕是有點奢望,來日方長。 下面是別人總結的,我照著debug了一次,

Kafka原始碼Sender分析

我們先來介紹一下Sender傳送訊息的整個流程:首先根據RecordAccumulator的快取情況,利用ready篩選出可以向哪些節點發送訊息,然後根據生產者和各個節點的連線愛你概況,過濾Node節點,之後,生成相應的請求,這裡要特別注意的是每個Node節點只

Kafka原始碼KafkaConsumer分析offset操作

當消費者正常消費過程中以及Rebalance操作開始之前,都會提交一次Offset記錄Consumer當前的消費位置。在SubscriptionState中使用TopicPartitionState記錄了每個TopicPartition的消費狀況,TopicPa

【簡記】Java Web 內幕——Spring原始碼(元件分析,BeanFactory原始碼,Bean建立之前)

本章內容: Bean元件、Context元件解析 BeanFactory的建立 初始化Bean例項之前的操作 Bean元件解析 Spring Bean 的建立是典型的工廠模式, 它的頂級介面是BeanFactory。 Bean工廠的類層次關係

spring原始碼—Assert.notNull

/*** Assert that an object is not <code>null</code> .* <pre class="code">Assert.notNull(clazz, "The class must not be null");</pre>

Spring原始碼ApplicationContext(九)初始化剩餘的單例

這裡所指的剩餘的單例,其實就是非延遲載入單例。在Spring的原始碼中,是通過finishBeanFactoryInitialization的方法來執行的。我們按照慣例,先來看一張時序圖。(相關資源可到這裡下載:http://pan.baidu.com/s/1sjSo9a9

Spring原始碼總結與分析

前言 Spring是什麼?它是一個應用程式框架,為應用程式的開發提供強大的支援,例如對事務處理和持久化的支援等;它也是一個bean容器,管理bean物件的整個生命週期,維護bean的各種存在狀態,例如bean物件的例項化、銷燬、bean的單例項和多例項狀態等。 Spring作為Jav

Spring系列AOP分析對通知方法的執行過程(九)

轉載請註明出處:https://blog.csdn.net/zknxx/article/details/80261327 我們在上一篇文章中說到了前置通知的方法呼叫AspectJMethodBeforeAdvice#before,在這個before方法中又呼叫

深入理解Spring原始碼自動裝配

自動裝配;          Spring利用依賴注入(DI),完成對IOC容器中中各個元件的依賴關係賦值;    1)、@Autowired:自動注入:          1)、預設優先按照型別去容器中找對應的元件:applicationContext.getBean(Bo

Spring系列AOP分析獲取Advice的過程(四)

我們在前面的文章中分析了從切面類中獲取Advisor的過程,我們最後建立的Advisor例項為:InstantiationModelAwarePointcutAdvisorImpl,它是一個Advisor和PointcutAdvisor的實現類,所以我們可以從這

深入理解Spring原始碼bean的生命週期控制器BeanPostProcessor

 spring是藉助ioc容器進行bean的初始化的,ioc的概念如下: bean的生命週期:            bean建立---初始化----銷燬的過程   容器管理bean的生命週期;  我們可以自定義初始化和銷燬方法;容器在bean進行到當前生命週期

Spring原始碼解析(十)分析一個Spring迴圈引用失敗的問題

前言: 之前我們有分析過Spring是怎麼解決迴圈引用的問題,主要思路就是三級快取;Spring在載入beanA的時候會先呼叫預設的空建構函式(在沒有指定建構函式例項化的前提下)得到一個空的例項引用物件,這個時候沒有設定任何值,但是Spring會用快取把它給提

spring原始碼 getBean流程圖

spring-getBean方法流程 流程圖 Created with Raphaël 2.1.0呼叫getBean方法(1)(4)(5)(6) (14)end(7)(15)(8)(9) (10)(11)(12)yesnoyesnoyesnoyesn

Spring原始碼事件驅動模型

SpringContext中初始化事件釋出者 ### //spring初始化事件的地方 //spring初始化事件的地方 public abstract class AbstractApplicationContext extends DefaultResou

Spring原始碼bean的基本解析

先看這樣一段兒程式碼: spring bean xml配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchem

Spring原始碼自動裝配

引言 我們使用Spring開發過程中經常會用到Autowired註解注入依賴的bean,這部分也是面試的熱點問題之一。今天咱們一起來深入研究下自動注入的背後實現原理。首先上一個例子,如下所示: @RestController public class TestController { @Autowi