DAO層, 是每一個web後端程序員都繞不過去的一個話題java
DAO層負責的內容很重要也很單一, 從數據庫中讀取數據而後放到Model裏, 僅此而已git
說他難吧, 其實就是體力活, 何況在微服務架構下, 歷來都是單表查詢, 複雜SQL也不知道各位多久沒用過了.程序員
說他簡單吧, 寫SQL而後一個一個調用set方法, 也是個挺麻煩的事兒.github
因此市面上琳琅滿目的出現了一堆DAO層框架, 如今比較主流的有Mybatis, Spring Data Jpa, JdbcTemplate, 這些框架極大的簡化了咱們訪問數據庫的過程web
首先先明確一下DAO層具體負責哪些事情呢, 一共三點數據庫
Mybatis雖然目前已經霸佔了大部分互聯網公司的ORM層的位置, 提供了對動態SQL良好的支持後端
可是用Mybatis的同窗有沒有以爲每一個語句都要寫個XML很是麻煩呢, 而且查找問題的時候從接口找對應的XML語句不太方便呢?架構
有同窗會說了, 咱們有Mybatis Generator, 能夠自動生成XML文件, Mapper接口, 超級方便!!!app
嗯, Mybatis Generator 是個很是好的用於開發的框架, 能夠極大的簡化開發量, 我也用過一段時間, 可是因爲他是代碼生成框架的緣由, 項目裏會多很是多難以維護而且很是大的類, 好比Example , XML 什麼的, 有沒有有代碼潔癖的同窗看着很是不爽呢~框架
解決辦法有嗎? 有! 有同感的同窗能夠往下看
Jpa家族的老大哥(嗯...可能hibernate纔算?)
第一次看到這個框架的時候眼前一亮, 臥槽, 太tm方便了啊. 基本的CRDU都給提供好了, 一行代碼不用寫
可是當Jpa遇到動態SQL的狀況, 就很坑爹了
一句話形容: 初期Jpa一時爽, 動態SQL火葬場
那麼有沒有方法在保持Jpa的簡潔性的基礎上能夠很爽的寫動態SQL呢, 能! 有興趣的同窗能夠往下看
Spring原生自帶的Jdbc包裝層, 其實算不上ORM框架, 可是仍是有一些公司在使用
優勢呢, 固然是Spring全家桶自帶, SpringBoot的狀況下基本免配置, 接入起來很是方便, 從代碼量來講比Mybatis要少很多, 而且性能拔羣, 比Mybatis快很多
缺點也很是的明顯, 基本是Jdbc的進階版, 其他功能少的可憐, 基本是直接操做SQL, 查詢結果轉Java Model也要手動去寫代碼
那麼可不可讓JdbcTemplate用起來更方便一點, 減小一些手動編碼呢, 能夠!
其實咱們的需求很是簡單, 一共就四點
在這裏推薦一下個人ORM層開源項目fastdao-JdbcTemplatePlus
經過這個咱們能夠得到哪些好處呢
基本的CRUD功能由框架提供 首先基本的經過主鍵的CRUD由框架提供,不須要寫一行代碼, 同時包含了Mybatis Generator中實用的insertSelective
, updateSelective
方法,提供的方法以下
/** * insert DATA to db, all field will be set include null * if you want to update only when none of field is null you can use this method */
int insert(DATA model);
/** * insert DATA to db, only non-null field will be insert * if you want null field to be default value in db ,you can use this method */
int insertSelective(DATA model);
/** * update DATA by primaryKey, all field will be set include null * make sure primaryKey in DATA is set */
int updateByPrimaryKey(DATA model);
/** * update DATA by primaryKey, only non-null field will be updated */
int updateByPrimaryKeySelective(DATA model);
/** * delete by primaryKey */
int deleteByPrimaryKey(PRIM_KEY primaryKey);
/** * select by primaryKey */
DATA selectByPrimaryKey(PRIM_KEY primaryKey);
/** * multiple selection */
List<DATA> selectByPrimaryKeys(Collection<PRIM_KEY> primaryKeys);
/** * multiple selection */
List<DATA> selectByPrimaryKeys(PRIM_KEY... primaryKeys);
複製代碼
那麼有些小夥伴要問了, 若是我須要自定義查詢怎麼辦, 好比按照用戶名搜索用戶?, 這裏好像不支持啊
不支持是不可能的, 可是這裏須要一些額外的開發量, 也是這個框架惟一須要引入額外類的地方
這個框架經過如下方法支持自定義操做
/** * update by UpdateRequest * if you want to update only a few field, or want to update by condition, you can use this method */
int update(UpdateRequest updateRequest);
/** * delete by condition */
int delete(DeleteRequest deleteRequest);
/** * count by condition */
int count(CountRequest countRequest);
/** * select by QueryRequest * this method doesn't support extra function */
List<DATA> select(QueryRequest queryRequest);
}
複製代碼
舉個例子, 好比咱們的User
Model類定義以下
@Data
@ToString
public class User {
private Long id;
private String name;
private Date updated;
private Date created;
private Boolean deleted;
}
複製代碼
這裏須要小夥伴們額外定義一個操做類, 其實就是定義一下Model類的每一個字段和數據庫字段之間的定義關係,是否是很簡單
public class Columns {
public static final Column ID = new Column("id");
public static final Column NAME = new Column("name");
public static final Column UPDATED = new Column("updated");
public static final Column CREATED = new Column("created");
public static final Column DELETED = new Column("deleted");
}
複製代碼
那麼怎麼寫自定義查詢呢, 好比說須要按照name
查找User
, 只要一行代碼
public List<User> selectByName(String name){
return dao.select(QueryRequest.newInstance().setCondition(NAME.eq(name)));
}
複製代碼
是否是很簡單呢, 這裏QueryRequest
等同於一個SQL查詢的實體, setCondition
操做添加了這個查詢的條件, NAME
就是咱們剛纔定義的哪一個操做類的字段了. 那麼其餘操做呢? 好比只更新指定字段的操做能完成嗎 ? 固然! 好比說若是隻須要對name
字段作更新的話 能夠這麼寫, 更新指定id的name
字段
public void updateName(Long id,String name){
UpdateRequest request=UpdateRequest.newInstance().addUpdateField(NAME,name).setCondition(ID.eq(id));
dao.update(request);
}
複製代碼
那麼動態SQL語句應該怎麼寫呢, 將剛纔的例子擴展一下, 若是須要按照 name
和 created
的範圍來查詢 User
(兩個都是可選條件), 而且須要按照建立時間排序和分頁.
public List<User> selectByNameAndCreated(String name,Date createdStart,Date createdEnd){
Condition condition = Condition.and()
.andOptional(NAME.eq(name))
.andOptional(CREATED.gt(createdStart).lt(createdEnd))
.allowEmpty();
dao.select(QueryRequest.newInstance().setCondition(condition).addSort(CREATED,OrderEnum.DESC).offset(0).limit(20));
}
複製代碼
解釋一下, Condition
是一個查詢條件類,Condition.and()
建立了一個多條件查詢條件類, andOptional
爲這個類添加可選條件, 什麼是可選條件呢,好比說NAME.eq(name)
這個條件,當name
爲null
的時候, 這個條件將被排除掉,created
的條件同理, allowEmpty()
表示該條件容許爲空, 也就是說是否容許SELECT * FROM table
這樣的語句, 第二行的設置排序規則, offset
,limit
聰明的小夥伴應該一眼就能看懂, 應該不用我多解釋了~(or
語句也是支持的)
是否是感受可讀性還能夠, 而且用起來很方便呢
相信小夥伴們也能看出來, 這個是框架天然支持的, 同時, 框架支持一些額外的操做,好比說單個Model查詢, 單字段查詢, 一鍵分頁查詢, ON DUPLICATE KEY
,聚合函數等
int insert(InsertRequest insertRequest, Consumer<Number> doWithGeneratedKeyOnSuccess);
/** * select by QueryRequest * if has multiple result, only return first row */
DATA selectOne(QueryRequest queryRequest);
/** * select by Single Field */
<T> List<T> selectSingleField(QueryRequest queryRequest, Class<T> clazz);
/** * support SqlFunction as request field */
List<QueryResult<DATA>> selectAdvance(QueryRequest queryRequest);
/** * select by a page, and count result will set to page */
List<DATA> selectPage(QueryRequest request, Page page);
複製代碼
insert
接口目前能夠支持 ON DUPLICATE KEY
操做 selectOne
接口主要用於一些惟一索引查詢, 最終只會返回一個Model selectSingleField
接口用於盡須要某個字段的查詢(能夠是聚合函數)(須要多個字段的狀況下能夠用通用的select(QueryRequest request)
方法手動指定須要查詢的字段, 默認爲全字段查詢) selectAdvance
用於支持GROUP BY
HAVING
等須要聚合函數做爲字段的狀況 selectPage
能夠自動執行count
語句, 並將總的條目數 set 到Page
中
通過測試, 在不使用插件的狀況下, 查詢的執行性能是Mybatis 的 三倍左右, 可是因爲是基於jdbcTemplate的框架, 比jdbcTemplate 慢 50%左右(不要介意插件是什麼~詳細解釋能夠解釋去github上看哈)
項目目前已經開源, 歡迎你們嘗試使用, 喜歡的話能夠點個STAR支持一下,也但願多多提意見,ISSUES~ 詳細的使用和接入方法請參考github的README-CN
項目地址 : jdbcTemplatePlus項目地址
因爲我一直在互聯網公司任職, 而且目前互聯網公司都採用單表SQL查詢, 多表聯合的狀況每每經過程序編碼進行聯合查詢, 因此對多表聯合的場景並無進行良好的支持(其實連支持都沒有), 若是由這類需求的小夥伴能夠在github上提ISSUE,若是需求比較普遍的話, 會按照Jpa註解風格提供對聯合查詢的支持的~
感謝閱讀~