聽說過spring-data-jdbc麼?來個最佳實踐

很多人知道Mybatis,知道Jpa,但對2019年新誕生的一門技術知之甚少。那就是:

spring-data-jdbc

。這個標題起的很普通,但是內容絕對是最新的。

注意我們這裡說的是

data-jdbc

,而不是普通的

jdbc

。它擁有了類似

jpa

的一些特性,比如能夠根據方法名推匯出sql,基本的CRUD等,也擁有了寫原生sql的能力。

最為關鍵的是,它非常的清爽,不需要依賴hibernte或者jpa。

千呼萬喚始出來,使用了一下,真是驚豔。它們的關係可以看下面這張圖。

聽說過spring-data-jdbc麼?來個最佳實踐

可以看到spring-data-jdbc是和spring-data-jpa一樣,同屬於spring-data系列的。下面我們就來實踐一把,來看一下它的最佳實踐。

1. 配置準備工作

建立好Springboot專案之後,需要加入spring-data-jdbc的依賴。

org。springframework。boot spring-boot-starter-data-jdbc

為了方便演示,我這裡使用的是h2資料庫。可以在springboot中配置開啟它的web配置端。

h2: console: enable: true path: /h2-console settings: trace: true web-allow-others: true

啟動之後,就可以透過下面的地址訪問h2的console。

聽說過spring-data-jdbc麼?來個最佳實踐

可以看到裡面有一張叫做goods_basic的表。它是怎麼建立進去的呢?先來看一下我們的datasource配置。

spring: datasource: driver-class-name: org。h2。Driver url: jdbc:h2:mem:test;MODE=MYSQL;CASE_INSENSITIVE_IDENTIFIERS=TRUE; schema: classpath:sql/h2/schema。sql #data: classpath:sql/h2/data。sql

其中, spring。datasource。schema所指定的sql檔案,將會在專案啟動的時候,自動執行,這當然也是有

AutoConfigure

來完成的。來看一下我們的建表語句。

CREATE TABLE `goods_basic` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `code` varchar(255) NOT NULL DEFAULT ‘’ COMMENT ‘編碼,不可重複;’, `barcode` varchar(255) NOT NULL COMMENT ‘69開頭的13位標準碼’, `short_name` varchar(255) NOT NULL COMMENT ‘商品名稱’, `photos` json DEFAULT NULL COMMENT ‘商品圖片’, `properties` json NOT NULL COMMENT ‘商品屬性,或者規格’, `unit` varchar(8) NOT NULL COMMENT ‘單位;最多8個位元組’, `state` tinyint NOT NULL DEFAULT ‘1’, `created` datetime NOT NULL COMMENT ‘建立時間’, `modified` datetime NOT NULL COMMENT ‘更新時間’, `version` bigint NOT NULL DEFAULT ‘0’ COMMENT ‘樂觀鎖版本號’, PRIMARY KEY (`id`), UNIQUE KEY `idx_account_role` (`code`,`barcode`) USING BTREE) ;

可以看到是徹頭徹尾的mysql語法。透過在h2裡面指定

MODE=MYSQL

屬性,就可以把h2切換到mysql的語法。雖然h2在專案實際執行中感覺總是差那麼一點意思,但對於測試來說,不得不說是個好工具。

到此為止,我們的準備工作就完成了,可以看到就是普通的datasource配置,簡單的很。

2.如何啟用spring-data-jdbc?

由於我們在前面引入的是starter的jar包,那就代表一些配置某人就在後臺完成了。下面來看一下,建立一個Dao(Repository),是有多簡單。

沒錯,我們只需要繼承

PagingAndSortingRepository

或者

CrudRepository

,就可以了,和jpa的一樣。

org。springframework。data。repository。PagingAndSortingRepositoryorg。springframework。data。repository。CrudRepository

看一下上面的路徑,和jpa和jdbc是沒什麼關係的,這就是spring-data抽象層的強大之處。

看一下我下面定義的這個dao,它實際上表現了常見的四種書寫方式。

聽說過spring-data-jdbc麼?來個最佳實踐

/** * Copyright (c) 2020。 All Rights Reserved。 * @author yuanluoji * @date 2020/10/16 */public interface GoodsBasicRepository extends PagingAndSortingRepository, Complex { List findByCode(String code); @Query(“select * from goods_basic where code=:code”) List findByCode2(String code);}

2.1 預設的CRUD

當你繼承了CrudRepository這個介面,就預設已經有了CRUD的能力,你可以呼叫save,findAll等方法,直接獲取對實體的讀寫,無需再做任何對映。

這比MyBatis還要簡單方便,因為MyBatis你要

不的不

上一個MyBatisPlus才能得到相同的功能。

當你繼承了PagingAndSortingRepository介面,除了擁有CRUD,還擁有了分頁的功能。

2.2 根據方法名直接查詢

有一段時間,使用jpa,可以直接根據規則寫方法名,不用寫任何SQL,就可以完成查詢功能。這個現在在jdbc中也有了。

程式碼中的

findByCode

方法,意思就是根據

code

,來查詢當前實體。這個過程將被翻譯成:

select * from goods_basic where code = :code

我們無需多些任何sql。下面,就是一張基本的對映表。 這可都是標準sql哦,都可以在方法名中完成。

聽說過spring-data-jdbc麼?來個最佳實踐

2.3 使用Query註解

@Query(“select * from goods_basic where code=:code”) List findByCode2(String code);

如果條件很多,這個方法名將會變得很長很長。在service層呼叫的時候你會一直喊

臥槽

這種複雜查詢語句,你可能需要使用Query註解來完成。寫在接口裡的方法,此時將失去語意表達的意義。你可以使用任意的方法名,只需要把你的sql寫在註解裡就可以了。

2.4 靈活的自定義

但對於想要靈活的控制sql行為的應用來說,上面這幾種方式就不行了。

比如,根據輸入條件的有無,做一些邏輯判斷。做一些類似mybatis的動態sql,或者使用StringBuilder來拼接一些sql。

注意我們的基礎Dao,繼承了一個介面,叫做

Complex

。spring-data-jdbc約定,這個介面的實現,放在ComplexImpl中,否則就會報錯。所以,這又是一個約定所實現的魔法。

我們定定義了一個test方法,期望透過傳入的code獲取一個列表。

public interface Complex { List test(String code);}

為了支援這種自定義查詢,我做了一個基類。裡面注入了一個jdbc的模版類,還注入了一個

JdbcAggregateOperations

public abstract class BaseRepository { /** * 高度封裝的JDBC操作,可直接儲存實體 */ @Getter @Autowired private JdbcAggregateOperations operations; /** * 普通命名的JDBC查詢和操作,可使用 {@link org。springframework。jdbc。core。BeanPropertyRowMapper} * 完成高階自動裝配,可自動完成駝峰和下劃線的自動對映 */ @Getter @Autowired private NamedParameterJdbcTemplate template;}

來看一下我們的實現類。

public class ComplexImpl extends BaseRepository implements Complex { @Override public List test(String code) { StringBuilder sb = new StringBuilder(“select * from goods_basic”); Map params = new HashMap(); if (!StringUtils。isEmpty(code)) { sb。append(“ where code=:code”); params。put(“code”, code); } List x = getTemplate()。query(sb。toString(), params, new BeanPropertyRowMapper<>(GoodsBasic。class)); return x; }}

使用

BeanPropertyRowMapper

,就可以避免繁瑣的屬性複製,程式碼變的非常清爽。

3. 實體配置

很多時候,實體有許多的通用屬性。這就需要抽取出來,在外面進行自定義。下面是我定義的一個基本的實體。包含id、建立愛你更新時間以及一個樂觀鎖版本號。這裡的Id註解是

org。springframework。data。annotation。Id

,而不是javax的。

@Datapublic abstract class AbstractEntity { @Id private Long id; private Date created; private Date modified; @Version private Long version;}

裡面的created和modified欄位,如果忘了寫怎麼辦?難道要寫一個過濾器麼?

不需要那麼麻煩,我們可以追加一個callback。

下面的程式碼,就是一個自動新增更新時間的例子。非常的好用。

@Configurationpublic class DataJdbcConfiguration extends AbstractJdbcConfiguration { @Bean public BeforeSaveCallback absEntityBeforeSet() { return (entity, aggregateChange) -> { entity。setModified(new Date()); return entity; }; }}

4. 小結

spring-data-jdbc是一個比較新的技術,現在的實踐文章還是很少。小Q在這裡嘗試了一個語句的四種寫法,對此還是深有感慨的。

現在的技術框架,背後做了很多工作,靠約定實現了很多功能。我來發表一下對於這些sql寫作方式的見解。

1.CRUD方式

這個很簡單,在不同的ORM框架下遷移也很方便,如果沒有其他必要,建議只需要繼承一個介面類就可以了。

2.根據方法名查詢

這個在引數比較少的時候,比較推薦,因為很清晰,也能在jpa之間進行切換。

3.使用Query

對於稍微複雜的sql,建議使用這種方式。和jpa的寫法一樣,jpa中開啟nativeSQL,和它的效果是一樣的。

4.靈活自定義

這個約定方式不錯,適合非常複雜的業務邏輯場景。

5.QueryDSL

querydsl作為一門通用的查詢語言,用在Spring data jdbc上,也是可以的。但它要生成一些額外的程式碼,個人比較有潔癖,暫未使用。

可以說,有了sping-data-jdbc,又不需要ORM去給你做什麼快取,用起來真是爽呆了。

再次提醒,本文的完整示例程式碼,見github倉庫。

https://github。com/yuanluoji/purestart-springboot-data-jdbc