Spring原始碼之JdbcTemplate分析
JdbcTemplate
用過Spring開發的,ORM框架一般選擇MyBatis或者Hibernate,不過,Spring對JDBC API的封裝工具JdbcTemplate,也提供了很方便的操作,不需要再在使用jdbc api時捕獲那麼多受檢異常,忍受那麼多樣板式的程式碼了。
JdbcTemplate主要提供以下幾類方法:
- execute方法:用於執行任何SQL語句,一般用於執行DDL語句;
- update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate批量執行修改;
- query方法及queryForXXX方法:用於執行查詢相關語句,query方法返回一個列表,queryForXXX方法一般只返回一個物件;
- 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 Boot之JdbcTemplate多資料來源配置與使用
之前在介紹使用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