Spring詳解8.Spring DAO

一年又一年抓督,字節(jié)跳動(dòng) Lark(飛書) 研發(fā)團(tuán)隊(duì)又雙叒叕開始招新生啦妒貌!
【內(nèi)推碼】:GTPUVBA
【內(nèi)推鏈接】:https://job.toutiao.com/s/JRupWVj
【招生對(duì)象】:20年9月后~21年8月前 畢業(yè)的同學(xué)
【報(bào)名時(shí)間】:6.16-7.16(提前批簡(jiǎn)歷投遞只有一個(gè)月抓住機(jī)會(huì)哦G【亍)
【畫重點(diǎn)】:提前批和正式秋招不矛盾晌块!面試成功喇嘱,提前鎖定Offer信夫;若有失利仿野,額外獲得一次面試機(jī)會(huì)铣减,正式秋招開啟后還可再次投遞。

點(diǎn)擊進(jìn)入我的博客

更多章節(jié)

Spring詳解1.概述
Spring詳解2.理解IoC容器
Spring詳解3.Bean的裝配
Spring詳解4.容器內(nèi)幕
Spring詳解5.AOP
Spring詳解6.基于AspectJ的AOP
Spring詳解7.Spring MVC
Spring詳解8.Spring DAO

1 Spring DAO的概念

什么是DAO

DAO(Data Access Object)是用于訪問(wèn)數(shù)據(jù)的對(duì)象脚作,雖然在大多數(shù)情況下將數(shù)存在數(shù)據(jù)庫(kù)中徙歼,但這并不是唯一的選擇,也可以將數(shù)據(jù)存儲(chǔ)到文件中或LDAP中鳖枕。DAO不但屏蔽了數(shù)據(jù)存儲(chǔ)的最終介質(zhì)的不同魄梯,也屏蔽了具體的實(shí)現(xiàn)技術(shù)的不同。提供DAO層的抽象可以帶來(lái)一些好處:可以很容易地構(gòu)造模擬對(duì)象宾符,方便單元測(cè)試的開展酿秸;在使用切面時(shí)會(huì)有更多的選擇,既可以使用JDK動(dòng)態(tài)代理魏烫,又可以使用 CGLib動(dòng)態(tài)代理辣苏。

Spring DAO的內(nèi)容
  • Spring對(duì)多個(gè)持久化技術(shù)提供了集成支持,包括 Hibernate哄褒、 MyBatis稀蟋、JPA、JDO呐赡;
  • Spring提供一個(gè)簡(jiǎn)化JDBC API操作的Spring JDBC框架退客。
  • Spring面向DAO制定了一個(gè)通用的異常體系,屏蔽具體持久化技術(shù)的異常链嘀,使業(yè)務(wù)層和具體的持久化技術(shù)實(shí)現(xiàn)解耦萌狂。
  • Spring提供了模板類簡(jiǎn)化各種持久化技術(shù)的使用。

2 Spring統(tǒng)一的異常體系

數(shù)據(jù)訪問(wèn)異常DataAccessException

Spring在org.springframework.dao包中提供了一套完備優(yōu)雅的DAO異常體系怀泊,這些異常都繼承于 DataAccessException茫藏,而DataAccessException本身又繼承于NestedRuntimeException,NestedRuntime Exception異常以嵌套的方式封裝了源異常霹琼。因此务傲,雖然不同持久化技術(shù)的特定異常被轉(zhuǎn)換到 Spring的DAO異常體系中凉当,但原始的異常信息并不會(huì)丟失;只要用戶愿意,就可以方便地通過(guò)getCause()方法獲取原始的異常信息售葡。

異常體系
Spring DAO 異常體系第一層次的異常類

Spring以分類手法建立了異常分類目錄看杭,上圖列出了那些位于Spring DAO異常體系第一層次的異常類,每個(gè)異常類下可能擁有眾多的子異常類天通。

異常 說(shuō)明
CleanupFailureDataAccessException DAO操作成功執(zhí)行,但在釋放數(shù)據(jù)資源時(shí)發(fā)生異常熄驼,如關(guān)閉 Connection時(shí)發(fā)生異常等
ConcurrencyFailureException 表示在進(jìn)行并發(fā)數(shù)據(jù)操作時(shí)發(fā)生異常,如樂觀鎖無(wú)法獲取像寒、悲觀鎖無(wú)法獲取、死鎖引發(fā)的失敗等
DataAccessResourceFailureException 訪問(wèn)數(shù)據(jù)資源時(shí)失敗瓜贾,如無(wú)法獲取數(shù)據(jù)連接诺祸、無(wú)法獲取Hibernate的會(huì)話等
DataRetrievalFailureException 獲取數(shù)據(jù)失敗,如找不到對(duì)應(yīng)主鍵的數(shù)據(jù)祭芦、使用了錯(cuò)誤的列索引等
DataSourceLookupFailure Exception 無(wú)法從JNDI中查找到數(shù)據(jù)源
DatalntegrityViolationException 當(dāng)數(shù)據(jù)操作違反了數(shù)據(jù)一致性限制時(shí)拋出的異常,如插入重復(fù)的主鍵筷笨、引用不存在的外鍵等
InvalidDataAccessApiUsageException 不正確地調(diào)用某一持久化技術(shù)時(shí)拋出的異常,如在 Spring JDBC中查詢對(duì)象龟劲,在調(diào)用前必須進(jìn)行編譯操作,如果忘記這項(xiàng)操作則會(huì)產(chǎn)生該異常胃夏。這種異常不是由底層數(shù)據(jù)資源產(chǎn)生的,而是由不正確地使用持久化技術(shù)產(chǎn)生的
InvalidDataAccessResourceUsageException 在訪問(wèn)數(shù)據(jù)源時(shí)使用了不正確的方法所拋出的異常昌跌,如SQL語(yǔ)句錯(cuò)誤將拋出該異常
PermissionDeniedDataAccessException 數(shù)據(jù)訪問(wèn)時(shí)由于權(quán)限不足引發(fā)的異常仰禀,如僅擁有只讀權(quán)限的用戶試圖進(jìn)行數(shù)據(jù)更改操作時(shí)將拋出該異常
UncategorizedAccessException 其他未分類的異常都?xì)w到該異常中
JDBC異常轉(zhuǎn)換器
  • 傳統(tǒng)的JDBC API在發(fā)生幾乎所有的數(shù)據(jù)操作問(wèn)題時(shí)都會(huì)拋出相同的SQLException,它將異常的細(xì)節(jié)性信息封裝在異常屬性中蚕愤。SQLException擁有兩個(gè)代表異常具體原因的屬性:錯(cuò)誤碼和SQL狀態(tài)碼答恶。
  • Spring根據(jù)錯(cuò)誤碼和SQL狀態(tài)碼信息將SQLException譯成 Spring DAO的異常體系所對(duì)應(yīng)的異常。org.springframework.jdbc.support.SQLExceptionTranslator接口的兩個(gè)實(shí)現(xiàn)類SQLErrorCodeTranslator和SQLStateSQLExceptionTranslator分別負(fù)責(zé)處理SQLException中錯(cuò)誤碼和SQL狀態(tài)碼的翻譯工作萍诱。
其他持久化技術(shù)的異常轉(zhuǎn)換器

由于各種框架級(jí)的持久化技術(shù)都擁有一個(gè)語(yǔ)義明確的異常體系悬嗓,所以將這些異常轉(zhuǎn)換為Spring DAO的體系相對(duì)輕松一些。在org.springframework.orm包中裕坊,分別為Spring所支持的ORM持久化技術(shù)定義了一個(gè)子包包竹,在這些子包中提供相應(yīng)ORM技術(shù)的整合類。

ORM持久化技術(shù) 異常轉(zhuǎn)換器
Hibernate org.springframework.orm.hibernate.Session.FactoryUtils
JPA org.springframework.orm.jpa.EntityManagerFactoryUtils
JDO org.springframework.orm.jdo.PersistenceManagerFactoryUtils
MyBatis MyBatis拋出的異常是和JDBC相同的SQLException異常籍凝,所以直接采用和JDBC相同的異常轉(zhuǎn)換器

3 統(tǒng)一數(shù)據(jù)訪問(wèn)模板

訪問(wèn)數(shù)據(jù)庫(kù)的流程

以JDBC為例映企,訪問(wèn)數(shù)據(jù)庫(kù)的操作大致按照以下流程進(jìn)行:準(zhǔn)備資源、啟動(dòng)事務(wù)静浴、在事務(wù)中執(zhí)行具體的數(shù)據(jù)訪問(wèn)操作堰氓、提交/回滾事務(wù)、關(guān)閉資源及處理異常苹享。而其中除了在事務(wù)中執(zhí)行具體的數(shù)據(jù)訪問(wèn)操作是業(yè)務(wù)相關(guān)的双絮,其他代碼都是幾乎固定不變的浴麻。

Spring DAO的模板

Spring將這個(gè)相同的數(shù)據(jù)訪問(wèn)流程固化到模板類中,并將數(shù)據(jù)訪問(wèn)中固定和變化的部分分開囤攀,同時(shí)保證模板類是線程安全的软免,以便多個(gè)數(shù)據(jù)訪問(wèn)線程共享同一個(gè)模板實(shí)例。固定的部分在模板類中已經(jīng)準(zhǔn)備好焚挠,而變化的部分通過(guò)回調(diào)接口開放出來(lái)膏萧,用于定義具體數(shù)據(jù)訪問(wèn)和結(jié)果返回的操作。這樣蝌衔,只要編寫好回調(diào)接口榛泛,并調(diào)用模板類進(jìn)行數(shù)據(jù)訪問(wèn),就可以得到預(yù)想的結(jié)果噩斟。


Spring DAO的模板和回調(diào)
不同持久化技術(shù)的模板類

Spring為各種支持的持久化技術(shù)都提供了簡(jiǎn)化操作的模板和回調(diào)曹锨,在回調(diào)中編寫具體的數(shù)據(jù)操作邏輯,使用模板執(zhí)行數(shù)據(jù)操作剃允。

ORM持久化技術(shù) 模板類
JDBC org.springframework.jdbc.core.JdbcTemplate
Hibernate org.springframework.orm.hibernate.HibernateTemplate
JPA org.springframework.orm.JpaTemplate
JDO org.springframework.orm.jdo.JdoTemplate

如果直接使用模板類沛简,則一般需要在DAO中定義一個(gè)模板對(duì)象并提供數(shù)據(jù)資源。Spring為每種持久化技術(shù)都提供了支持類斥废,支持類中已經(jīng)完成了這樣的功能椒楣。這樣,只需擴(kuò)展這些支持類牡肉,就可以直接編寫實(shí)際的數(shù)據(jù)訪問(wèn)邏輯撒顿,因此更加方便。這些支持類都繼承于dao.support.DaoSupport類荚板,DaoSupport類實(shí)現(xiàn)了InitializingBean接口凤壁,在afterPropertiesSet()接口方法中檢査模板對(duì)象和數(shù)據(jù)源是否被正確設(shè)置,否則將拋出異常跪另。

ORM持久化技術(shù) 支持類
JDBC org.springframework.jdbc.core.JdbcDaoSupport
Hibernate org.springframework.orm.hibernate.HibernateDaoSupport
JPA org.springframework.orm.jpa.JpaDaoSupport
JDO org.springframework.orm.jdo.JdoSupport

4 數(shù)據(jù)源

不管采用何種持久化技術(shù)拧抖,都必須擁有數(shù)據(jù)連接。在 Spring中免绿,數(shù)據(jù)連接是通過(guò)數(shù)據(jù)源獲得的唧席。在Spring中,不但可以通過(guò)JNDI獲取應(yīng)用服務(wù)器的數(shù)據(jù)源嘲驾,也可以直接在Spring容器中配置數(shù)據(jù)源淌哟,還可以通過(guò)代碼的方式創(chuàng)建一個(gè)數(shù)據(jù)源,以便進(jìn)行無(wú)容器依賴的單元測(cè)試辽故。

配置DBCP數(shù)據(jù)源
@Bean(destroyMethod = "close")
    public BasicDataSource basicDataSource() {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3306/test");
        basicDataSource.setUsername("root");
        basicDataSource.setPassword("password");
        return basicDataSource;
    }
  • BasicDataSource提供了close方法關(guān)閉數(shù)據(jù)源徒仓,所以必須設(shè)定destroyMethod為close,以便 Spring容器關(guān)閉時(shí)誊垢,數(shù)據(jù)源能夠正常關(guān)閉掉弛。
  • 假設(shè)數(shù)據(jù)庫(kù)是 MySQL症见,如果數(shù)據(jù)源配置不當(dāng),則將可能發(fā)生經(jīng)典的8小時(shí)問(wèn)題——原因是MySQL在默認(rèn)情況下如果發(fā)現(xiàn)一個(gè)連接的空閑時(shí)間超過(guò)8小時(shí)殃饿,則將會(huì)在數(shù)據(jù)庫(kù)端自動(dòng)關(guān)閉這個(gè)連接谋作。而數(shù)據(jù)源并不知道這個(gè)連接已經(jīng)被數(shù)據(jù)庫(kù)關(guān)閉了,當(dāng)它將這個(gè)無(wú)用的連接返回給某個(gè)DAO時(shí)乎芳,DAO就會(huì)報(bào)無(wú)法獲取 Connection的異常遵蚜。
配置C3P0數(shù)據(jù)源

C3P0是一個(gè)開放源碼的JDBC數(shù)據(jù)源實(shí)現(xiàn)項(xiàng)目,實(shí)現(xiàn)了JDBC3和JDBC2擴(kuò)展規(guī)范說(shuō)明的Connection和Statement池奈惑。ComboPooledDataSource也提供了一個(gè)用于關(guān)閉數(shù)據(jù)源的close方法吭净,這樣就可以保證 Spring容器關(guān)閉時(shí)數(shù)據(jù)源能夠被成功釋放。C3P0擁有比DBCP更豐富的配置屬性携取,通過(guò)這些屬性,可以對(duì)數(shù)據(jù)源進(jìn)行各種有效的控制攒钳。

配置Druid數(shù)據(jù)源

Druid首先是阿里巴巴開源的一個(gè)數(shù)據(jù)庫(kù)連接池帮孔,但它不僅僅是一個(gè)數(shù)據(jù)庫(kù)連接池雷滋,它還包含一個(gè)ProxyDriver,一系列內(nèi)置的JDBC組件庫(kù)文兢,一個(gè)SQL Parser晤斩。Druid是目前最好的數(shù)據(jù)庫(kù)連接池,在功能姆坚、性能澳泵、擴(kuò)展性方面,都超過(guò)其他數(shù)據(jù)庫(kù)連接池兼呵,包括DBCP兔辅、C3P0、BoneCP击喂、Proxool维苔、JBoss DataSource。

    @Bean
    public DruidDataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
使用配置文件放置屬性
@PropertySource(value = "classpath:database.properties")
public class DatabaseConfig {
    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
}
配置JNDI

如果應(yīng)用配置在高性能的應(yīng)用服務(wù)器上懂昂,則可能更希望使用應(yīng)用服務(wù)器本身提供的數(shù)據(jù)源介时。應(yīng)用服務(wù)器的數(shù)據(jù)源使用JNDI開放調(diào)用者使用,Spring為此專門提供了引用JDI數(shù)據(jù)源的JndiobjectFactoryBean類凌彬,通過(guò)jndiName指定引用的JNDI數(shù)據(jù)源名稱沸柔。

Spring數(shù)據(jù)源

Spring本身也提供了一個(gè)簡(jiǎn)單的數(shù)據(jù)源實(shí)現(xiàn)類DriverManagerDataSource,它位于org. springframework.jdbc.datasource包中铲敛。這個(gè)類實(shí)現(xiàn)了Javax.sql.DataSource接口褐澎,但它并沒有提供池化連接的機(jī)制;每次調(diào)用 getConnectionO方法獲取新連接時(shí)伐蒋,只是簡(jiǎn)單地創(chuàng)建一個(gè)新的連接乱凿。因此顽素,這個(gè)數(shù)據(jù)源類比較適合在單元測(cè)試或簡(jiǎn)單的獨(dú)立應(yīng)用中使用,因?yàn)樗恍枰~外的依賴類徒蟆。

5 Spring JDBC

Spring JDBC是Spring所提供的持久層技術(shù)胁出。它的主要目的是降低使用 JDBC API的門檻,以一種更直接段审、更簡(jiǎn)潔的方式使用 JDBC API全蝶。在 Spring JDBC里,僅需做那些與業(yè)務(wù)相關(guān)的DML操作的事寺枉,而將資源獲取抑淫、Statement創(chuàng)建、資源釋放及異常處理等繁雜而乏味的工作交給Spring JDBC姥闪。

5.1 使用Spring JDBC

簡(jiǎn)單的例子
    private void func() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(); // (1)
        jdbcTemplate.setDataSource(dataSource);

        String sql = "INSERT INTO tb_name (name, value) VALUES ('Lucas', '26')";
        jdbcTemplate.execute(sql);
    }

上述示例中始苇,特意將數(shù)據(jù)源創(chuàng)建和模板實(shí)例創(chuàng)建的代碼都列在這個(gè)例子中,但在實(shí)際應(yīng)用中筐喳,用戶一般不會(huì)在DAO中做這些事情催式。由于JdbcTemplate是線程安全的,因而所有的DAO都可以共享同一個(gè) JdbcTemplate實(shí)例避归,這樣(1)的代碼就可以從DAO中移除了荣月,轉(zhuǎn)而在 Spring配置文件中統(tǒng)一定義即可。

在DAO中使用JdbcTemplate
@Configuration
public class DatabaseConfig {
    @Bean
    public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}

@Repository
public class MyTestDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void func() {
        jdbcTemplate.execute("INSERT INTO tb_name (name, value) VALUES ('Lucas', '26')");
    }
}
JdbcTemplate的屬性

JdbcTemplate擁有幾個(gè)可用于控制底層JDBC API的屬性

  • queryTimeout:設(shè)置 JdbcTemplate所創(chuàng)建的Statement查詢數(shù)據(jù)時(shí)的最大超時(shí)時(shí)間梳毙。默認(rèn)為0哺窄,表示使用底層JDBC驅(qū)動(dòng)程序的默認(rèn)設(shè)置。
  • fetchSize:設(shè)置底層的ResultSet每次從數(shù)據(jù)庫(kù)返回的行數(shù)账锹。該屬性對(duì)程序的性能影響很大萌业,如果設(shè)置過(guò)大,因?yàn)橐淮涡暂d入的數(shù)據(jù)都放到內(nèi)存中奸柬,所以內(nèi)存的消耗很大:反之生年,如果設(shè)置過(guò)小,從數(shù)據(jù)庫(kù)讀取的次數(shù)將增大鸟缕,也會(huì)影響性能晶框。默認(rèn)值為0,表示使用底層JDBC驅(qū)動(dòng)程序的默認(rèn)設(shè)置懂从。
  • maxRows:設(shè)置底層的Resultset從數(shù)據(jù)庫(kù)返回的最大行數(shù)授段。默認(rèn)值為0,表示使用底層JDBC驅(qū)動(dòng)程序的默認(rèn)設(shè)置番甩。
  • ignoreWarnings:是否忽略SQL的警告信息侵贵。默認(rèn)為tue,即所有的警告信息都被記錄到日志中缘薛,如果設(shè)置為fale窍育,則JdbcTemplate將拋出SQLWarningException卡睦。

5.2 數(shù)據(jù)操作

更改數(shù)據(jù)update

JdbcTemplate在內(nèi)部是通過(guò)PreparedStatement執(zhí)行SQL語(yǔ)句的,可以使用綁定了參數(shù)的SQL語(yǔ)句漱抓。

    private void func() {
        String value = "ZZX";
        // (1) 直接執(zhí)行SQL
        jdbcTemplate.update("UPDATE tb_name SET name = 'ZZX' WHERE id = '1'");
        // (2) 綁定參數(shù)的SQL
        jdbcTemplate.update("UPDATE tb_name SET name = ? WHERE id = ?", new Object[]{value, 2});
        // (3) 指定參數(shù)的參數(shù)類型
        jdbcTemplate.update("UPDATE tb_name SET name = ? WHERE id = ?", new Object[]{value, 3}, new int[]{Types.VARCHAR, Types.INTEGER});
    }
帶回調(diào)的update

需要指出的是表锻,在實(shí)際使用中,應(yīng)優(yōu)先考慮使用不帶回調(diào)接口的 JdbcTemplate方法乞娄。首先瞬逊,回調(diào)使代碼顯得臃腫;其次仪或,回調(diào)并不能帶來(lái)額外的好處确镊。當(dāng)使用簡(jiǎn)潔版的方法時(shí),JdbcTemplate會(huì)在內(nèi)部自動(dòng)創(chuàng)建這些回調(diào)實(shí)例范删。以下是帶回調(diào)帶update方法:

  • int update(String sql, PreparedStatementSetter pss):PreparedStatementSetter是一個(gè)回調(diào)接口蕾域,它定義了一個(gè)void set values( PreparedStatement ps)接口方法。 JabcTemplate使用SQL語(yǔ)句創(chuàng)建出 PreparedStatement實(shí)例后到旦,將調(diào)用該回調(diào)接口執(zhí)行綁定參數(shù)的操作旨巷。PreparedStatement綁定參數(shù)時(shí),參數(shù)索引從1開始而非從0開始厢绝。
  • int update(PreparedStatementCreator psc)PreparedStatementCreator也是一個(gè)回調(diào)接口契沫,它負(fù)責(zé)創(chuàng)建一個(gè)PreparedStatement實(shí)例带猴。該回調(diào)接口定義了一個(gè)PreparedStatement create PreparedStatement( Connection con)方法昔汉。
  • protected int update(PreparedStatementCreator psc, PreparedStatementSetter pss):聯(lián)合使用PreparedStatementCreator和 PreparedStatementSetter回調(diào)。
獲取自增主鍵

在JDBC3.0規(guī)范中拴清,當(dāng)新增記錄時(shí)靶病,允許將數(shù)據(jù)庫(kù)自動(dòng)產(chǎn)生的主鍵值綁定到Statement或PreparedStatement中。在使用 Statement時(shí)口予,可以通過(guò)以下方法綁定主鍵值:int executeUpdate(String sql, int autoGeneratedKeys)娄周;也可以通過(guò)Connection創(chuàng)建綁定自增主鍵值的PreparedStatement,如下:Preparedstatement prepareStatement(String sql, int autoGeneratedKeys)沪停。Spring利用這一技術(shù)煤辨,提供了一個(gè)可以返回新增記錄對(duì)應(yīng)主鍵值的方法:int update(Preparedstatementcreator psc, KeyHolder generatedKey Holder)

private void func() {
        String sql = "INSERT INTO tb_name (name) VALUES (?)";
        // 創(chuàng)建一個(gè)主鍵持有對(duì)象
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update((Connection con) ->  {
            PreparedStatement statement = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            statement.setString(1, "ZZX");
            return statement;
            }, keyHolder);
        // 獲取主鍵
        System.out.println(keyHolder.getKey().intValue());
    }
批量修改數(shù)據(jù)

如果需要一次性插入或更新多條記錄木张,最好的選擇是使用JdbcTemplate批量數(shù)據(jù)更改的方
法众辨。JdbcTemplate的兩個(gè)批量數(shù)據(jù)操作方法:

  • int[] batchUpdate(String[] sql):多條SQL語(yǔ)句組成一個(gè)數(shù)組(這些SQL語(yǔ)句不帶參數(shù)),該方法以批量方式執(zhí)行這些SQL語(yǔ)句舷礼。 Spring在內(nèi)部使用JDBC提供的批量更新API完成操作鹃彻。
  • int[] batchUpdate(String sql, BatchPreparedStatementSetter pss):使用該方法對(duì)于同
    一結(jié)構(gòu)的帶參SQL語(yǔ)句多次進(jìn)行數(shù)據(jù)更新操作。BatchPreparedStatementSetter是一次性批量提交數(shù)據(jù)的妻献,getSize()是整批的大小蛛株。
private void func() {
        String sql = "INSERT INTO tb_name (name) VALUES (?)";
        String[] values = {"A", "B", "C"};

        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, values[i]);
            }

            @Override
            public int getBatchSize() {
                return values.length;
            }
        });
    }
查詢數(shù)據(jù)——使用RowCallbackHandler

當(dāng)結(jié)果集中沒有數(shù)據(jù)時(shí)团赁,此時(shí)并不會(huì)拋出異常,而是此時(shí)RowCallbackHandler回調(diào)接口中定義的處理邏輯沒有得到調(diào)用谨履。

    private void func() {
        String sql = "SELECT * FROM tb_name WHERE id = 1";

        User user = new User();
        jdbcTemplate.query(sql, (ResultSet rs) -> {
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setValue(rs.getLong("value"));
        });
        System.out.println(user);
    }
批量查詢數(shù)據(jù)——使用RowCallbackHandler
private void func() {
        String sql = "SELECT * FROM tb_name";

        List<User> users = new ArrayList<>();
        jdbcTemplate.query(sql, (ResultSet rs) -> {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setValue(rs.getLong("value"));
            users.add(user);
        });
        System.out.println(users);
    }
查詢數(shù)據(jù)——使用RowMapper<T>
private void func() {
        String sql = "SELECT * FROM tb_name";

        List<User> users = jdbcTemplate.query(sql, (ResultSet rs, int rowNum) -> {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setValue(rs.getLong("value"));
            return user;
        });

        System.out.println(users);
    }
  • 從功能上來(lái)說(shuō)欢摄,RowCallbackHandler和RowMapper<T并沒有太大的區(qū)別,它們都用于定義結(jié)果集行的讀取邏輯笋粟,將 Resultset中的數(shù)據(jù)映射到對(duì)象或List中剧浸。
  • 通過(guò)JDBC查詢返回一個(gè)Resultset結(jié)果集時(shí),JDBC并不會(huì)一次性將所有匹配的數(shù)據(jù)都加載到JM中矗钟,而是只返回一批次的數(shù)據(jù)(由JDBC驅(qū)動(dòng)程序決定)唆香,當(dāng)通過(guò)ResultSet#next()游標(biāo)滾動(dòng)結(jié)果集超過(guò)數(shù)據(jù)范圍時(shí),JDBC再獲取一批數(shù)據(jù)吨艇。這樣以一種“批量化+串行化”的處理方式避免大結(jié)果集處理時(shí)JVM內(nèi)存的過(guò)大開銷躬它。
  • 當(dāng)處理大結(jié)果集時(shí),如果使用RowMapper东涡,那么采用的方式是將結(jié)果集中的所有數(shù)據(jù)都放到一個(gè)Lis<T>對(duì)象中冯吓,這樣將會(huì)占用大量的JVM內(nèi)存,甚至可能直接引發(fā)OutOfMemoryException異常疮跑。這時(shí)组贺,可使用RowCallbackHandler接口,在processRow()接口方法內(nèi)部一邊獲取數(shù)據(jù)一邊完成處理祖娘,這樣數(shù)據(jù)就不會(huì)在內(nèi)存中堆積失尖,可大大減少對(duì)JVM內(nèi)存的占用。
  • 采用RowMapper的操作方式是先獲取數(shù)據(jù)渐苏,然后再處理數(shù)據(jù)掀潮;而 RowCallbackHandler的操作方式是一邊獲取數(shù)據(jù)一邊處理,處理完就丟棄之琼富。因此仪吧,可以將 Row Mapper看作采用批量化數(shù)據(jù)處理策略,而 RowHandler則采用流化處理策略鞠眉。
查詢單值數(shù)據(jù)

如果查詢的結(jié)果集僅有一個(gè)值薯鼠,如SELECT COUNT(*) FROM tb_name,這時(shí)可以使用更簡(jiǎn)單的方式獲取結(jié)果集的值械蹋。 JdbcTemplate為獲取結(jié)果集中的單值數(shù)據(jù)提供了3組方法出皇,分別用于獲取int、long的單值朝蜘,其他類型的單值則以 Object類型返回恶迈。

調(diào)用存儲(chǔ)過(guò)程

JdbcTemplate提供了兩個(gè)調(diào)用存儲(chǔ)過(guò)程的接口方法,分別介紹如下。

  • <T> T execute(String callString, CallableStatementCallback<T> action):用戶通過(guò)callString參數(shù)指定調(diào)用存儲(chǔ)過(guò)程的SQL語(yǔ)句暇仲;第二個(gè)參數(shù)CallableStatementCallback<T>是一個(gè)回調(diào)接口步做,可以在接口的方法中進(jìn)行輸入?yún)?shù)綁定、輸出參數(shù)注冊(cè)及返回?cái)?shù)據(jù)處理等操作奈附。
  • <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action):該接口方法使用 CallableStatementCreator創(chuàng)建CallableStatement全度。CallableStatementCreator負(fù)責(zé)創(chuàng)建 CallableStatement實(shí)例、綁定參數(shù)斥滤、注冊(cè)輸出參數(shù)等工作将鸵,CallableStatementCallback<T>負(fù)責(zé)處理存儲(chǔ)過(guò)程的返回結(jié)果。 Spring提供了創(chuàng)建CallableStatementcreator的工廠類 CallableStatementCreatorFactory佑颇,通過(guò)該工廠類可以簡(jiǎn)化CallableStatementCreator的實(shí)例創(chuàng)建工作顶掉。

6 整合其他ORM框架

6.1 Spring ORM框架好處

  1. 方便基礎(chǔ)設(shè)施的搭建:對(duì)于不同的ORM框架,始終可以采用相同的方式配置數(shù)據(jù)源挑胸;Spring還為不同的ORM框架提供了相應(yīng)的FactoryBean痒筒,用以初始化ORM框架的基礎(chǔ)設(shè)施,可以將它們當(dāng)成普通的Bean對(duì)待茬贵。
  2. 異常封裝:Spring能夠轉(zhuǎn)換各種ORM框架所拋出的異常簿透,將ORM框架專有的或檢査型異常
    轉(zhuǎn)換為Spring DAO異常體系中的標(biāo)準(zhǔn)異常。
  3. 統(tǒng)一的事務(wù)管理:通過(guò)使用基于 Spring DAO模板的編程風(fēng)格解藻,甚至使用ORM框架原生的API,只要遵循 Spring所提出的很少的編程要求老充,就可以使用 Spring提供的事務(wù)管理功能。此外螟左,JDBC代碼能夠在事務(wù)級(jí)別上與用戶使用的ORM框架一起使用啡浊。這一功能對(duì)于諸如批量處理、LOB操作等并不適合單獨(dú)采用ORM完成的地方尤其有用路狮。
  4. 允許混合使用多個(gè)ORM框架:Spring在DAO異常虫啥、事務(wù)蔚约、資源等高級(jí)層次建立了抽象奄妨,因而可以讓業(yè)務(wù)層對(duì)DAO具體實(shí)現(xiàn)的技術(shù)不敏感,可以在底層選用適合的實(shí)現(xiàn)方式苹祟,甚至可以混合使用多種ORM
  5. 方便單元測(cè)試:Spring容器使得替換不同的實(shí)現(xiàn)和配置變得非常簡(jiǎn)單砸抛,這些內(nèi)容包括 HibernateSession Factory的位置、 JDBC DataSource树枫、事務(wù)管理器及映射對(duì)象的實(shí)現(xiàn)等直焙,這樣就很容易隔離并測(cè)試不同的DAO類。

6.2 整合Hibernate

后續(xù)在學(xué)習(xí)Hibernate時(shí)會(huì)詳細(xì)講解

6.3 整合MyBatis

詳見:MyBatis詳解8.集成Spring

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末砂轻,一起剝皮案震驚了整個(gè)濱河市奔誓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖厨喂,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件和措,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜕煌,警方通過(guò)查閱死者的電腦和手機(jī)派阱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)斜纪,“玉大人贫母,你說(shuō)我怎么就攤上這事『懈眨” “怎么了腺劣?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)因块。 經(jīng)常有香客問(wèn)我誓酒,道長(zhǎng),這世上最難降的妖魔是什么贮聂? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任靠柑,我火速辦了婚禮,結(jié)果婚禮上吓懈,老公的妹妹穿的比我還像新娘及皂。我一直安慰自己,他們只是感情好半醉,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布涂屁。 她就那樣靜靜地躺著,像睡著了一般甘穿。 火紅的嫁衣襯著肌膚如雪腮恩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天温兼,我揣著相機(jī)與錄音秸滴,去河邊找鬼。 笑死募判,一個(gè)胖子當(dāng)著我的面吹牛荡含,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播届垫,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼释液,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了装处?” 一聲冷哼從身側(cè)響起误债,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后寝蹈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糟袁,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年躺盛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了项戴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡槽惫,死狀恐怖周叮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情界斜,我是刑警寧澤仿耽,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站各薇,受9級(jí)特大地震影響项贺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜峭判,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一开缎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧林螃,春花似錦奕删、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至横漏,卻和暖如春谨设,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缎浇。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工扎拣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人华畏。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓鹏秋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亡笑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359