摘要:根據(jù)區(qū)域部門學(xué)科類別查詢出符合條件的考評(píng)人員。繼承接口我們的考評(píng)員倉庫原繼承,為了實(shí)現(xiàn)可以復(fù)雜查詢,繼承接口。實(shí)現(xiàn)規(guī)格接口為了規(guī)范,我們建了一個(gè)的查詢規(guī)格的包,將所有生成查詢規(guī)格的類放入該包中。
SpringData
Spring項(xiàng)目中,我們使用JPA進(jìn)行查詢,只需簡(jiǎn)單地繼承SpringData提供的接口即可實(shí)現(xiàn)強(qiáng)大的數(shù)據(jù)查詢功能。
之前的強(qiáng)檢器具統(tǒng)計(jì)管理用的僅僅是單表查詢,理解不深,這次開發(fā)的考評(píng)員綜合查詢涉及到了多個(gè)實(shí)體間的查詢,值得學(xué)習(xí),特此記錄。
以下是ER圖。
管理部門選擇本部門或其管轄的部門中的某人員作為該部門的考評(píng)人員。根據(jù)區(qū)域、部門、學(xué)科類別查詢出符合條件的考評(píng)人員。
概述復(fù)雜查詢,我們就需要SpringData提供的JpaSpecificationExecutor接口,這是該接口的源代碼。
public interface JpaSpecificationExecutor{ /** * Returns a single entity matching the given {@link Specification}. * * @param spec * @return */ T findOne(Specification spec); /** * Returns all entities matching the given {@link Specification}. * * @param spec * @return */ List findAll(Specification spec); /** * Returns a {@link Page} of entities matching the given {@link Specification}. * * @param spec * @param pageable * @return */ Page findAll(Specification spec, Pageable pageable); /** * Returns all entities matching the given {@link Specification} and {@link Sort}. * * @param spec * @param sort * @return */ List findAll(Specification spec, Sort sort); /** * Returns the number of instances that the given {@link Specification} will return. * * @param spec the {@link Specification} to count instances for * @return the number of instances */ long count(Specification spec); }
該接口中聲明的方法都有一個(gè)共同的參數(shù):Specification,翻譯過來就是規(guī)范的意思。
這是Specification接口源代碼。
public interface Specification{ /** * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given * {@link Root} and {@link CriteriaQuery}. * * @param root * @param query * @return a {@link Predicate}, must not be {@literal null}. */ Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb); }
Specification接口中有一個(gè)toPredicate方法,看不懂代碼我們可以看注釋。
Creates a WHERE clause for a query of the referenced entity in form of a {Predicate}
用謂詞的形式為引用的實(shí)體創(chuàng)建一個(gè)WHERE語句。
到這里,我們應(yīng)該明白了這個(gè)查詢的原理:我們先生成我們需要的謂語,然后用toPredicate將謂語生成為where語句,最后再使用我們Specification類型的where語句作為參數(shù)用JpaSpecificationExecutor接口中定義的方法進(jìn)行查詢。
繼承JpaSpecificationExecutor接口public interface ExaminerRepository extends CrudRepository, JpaSpecificationExecutor { }
我們的考評(píng)員倉庫原繼承CrudRepository,為了實(shí)現(xiàn)可以復(fù)雜查詢,繼承JpaSpecificationExecutor接口。
實(shí)現(xiàn)規(guī)格接口為了規(guī)范,我們建了一個(gè)spec的查詢規(guī)格的包,將所有生成查詢規(guī)格的類放入該包中。
/** * @author zhangxishuo on 2018/5/26 * 考評(píng)人員查詢規(guī)格 */ public class ExaminerSpecs { // 日志 private static final Logger logger = Logger.getLogger(ExaminerSpecs.class); public static Specificationbase(Department userDepartment, final Map map) { return new Specification () { @Override public Predicate toPredicate(Root root, CriteriaQuery> criteriaQuery, CriteriaBuilder criteriaBuilder) { return null; } }; } }
我們?cè)谶@個(gè)類中寫了一個(gè)base方法,用于根據(jù)傳入的參數(shù)生成一個(gè)查詢規(guī)格。
return語句中,我們new了一個(gè)接口,然后在new這個(gè)接口的同時(shí)對(duì)這個(gè)接口進(jìn)行實(shí)現(xiàn),實(shí)現(xiàn)接口中的toPredicate方法。
toPredicate中的三個(gè)參數(shù)意義。Root:查詢的模型;CriteriaQuery:標(biāo)準(zhǔn)查詢,用于查詢的;CriteriaBuilder:標(biāo)準(zhǔn)構(gòu)建,用戶構(gòu)建查詢語句的。
大家不要去糾結(jié)是直接new接口然后在花括號(hào)中實(shí)現(xiàn)還是寫一個(gè)類實(shí)現(xiàn)接口然后再去new?
這里說一下我自己的理解。其實(shí)這主要是看實(shí)際需要,如果這個(gè)接口的實(shí)現(xiàn)類需要被好多個(gè)其他類調(diào)用,為了代碼的復(fù)用,我們就會(huì)寫一個(gè)類去實(shí)現(xiàn)接口,就像我們后臺(tái)MVC架構(gòu)的業(yè)務(wù)邏輯層,因?yàn)橥粋€(gè)方法可能會(huì)被好多個(gè)控制器進(jìn)行調(diào)用,所以我們建一個(gè)Service接口,再建立一個(gè)Service接口實(shí)現(xiàn)類,然后其他類需要時(shí)進(jìn)行Autowire。
如果我們的方法不需要復(fù)用,那Android代碼就是我們最好的例子(Android中大量使用了new接口然后在new時(shí)去實(shí)現(xiàn)的寫法)。
public class MyActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.content_layout_id); final Button button = findViewById(R.id.button_id); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // Code here executes on main thread after user presses button } }); } }
這是Android官方文檔中對(duì)按鈕添加點(diǎn)擊事件的示例代碼,我們看到這里的View.OnClickListener()就是在new的時(shí)候再去實(shí)現(xiàn)的。其實(shí)這也不難理解,打開我們每天用的APP,幾乎每個(gè)按鈕,每個(gè)視圖的功能都是不一樣的,所以不需要進(jìn)行代碼復(fù)用。
構(gòu)建謂語謂語是什么呢?按我的理解,一個(gè)謂語就是一個(gè)條件。
logger.debug("構(gòu)建謂語,人員的部門的區(qū)域的id等于map中區(qū)域的id"); Predicate districtPredicate = criteriaBuilder .equal(root.join("qualifier") .join("department") .join("district") .get("id") .as(Long.class), ((District) map.get("district")) .getId());
我們使用criteriaBuilder的equal方法構(gòu)建了一個(gè)等于的條件(或謂語),該條件要求考評(píng)員(root)的人員的部門的區(qū)域的id轉(zhuǎn)化為Long類型的結(jié)果與傳入的區(qū)域的id相等。
類似的寫法,我們可以構(gòu)建出部門和學(xué)科的謂語。
連接謂語謂語創(chuàng)建完了,我們還需要將這些謂語進(jìn)行連接。
這里我們需要用到and條件,即區(qū)域符合且部門符合且學(xué)科類別符合。
為了方便構(gòu)建,這里參考之前的項(xiàng)目代碼構(gòu)建了一個(gè)用and拼接謂語的方法。
private Predicate predicate = null; private CriteriaBuilder criteriaBuilder; // 設(shè)置and謂語.注意,這里只能設(shè)置and關(guān)系的謂語,如果謂語為OR,則需要手動(dòng)設(shè)置 private void andPredicate(Predicate predicate) { // 如果傳入的謂語不為空 if (null != predicate) { if (null == this.predicate) { // 如果該方法之前沒有謂語,則直接賦值 this.predicate = predicate; } else { // 如果之前有謂語。則使用criteriaBuilder的與將已有謂語和新謂語用and連接 this.predicate = this.criteriaBuilder.and(this.predicate, predicate); } } }
這幾行代碼就體現(xiàn)了一名優(yōu)秀軟件工程師的素養(yǎng),優(yōu)秀的人在寫代碼之前就能遇見哪些功能是重復(fù)的,不需實(shí)現(xiàn)之后再將相同代碼分離重構(gòu)。
logger.debug("用and連接該謂語"); this.andPredicate(districtPredicate);CriteriaQuery
如果我們只是單純的查詢的話,沒有什么特殊要求的話,那我們直接就可以把我們的謂語返回。
return this.predicate;
沒錯(cuò),直接將謂語返回,debug的時(shí)候發(fā)現(xiàn)實(shí)際查詢時(shí)會(huì)獲取我們的謂語,并執(zhí)行query.where語句。
如果需要復(fù)雜功能的話,可以使用CriteriaQuery。
// 把Predicate應(yīng)用到CriteriaQuery中去,可以實(shí)現(xiàn)更豐富的查詢功能,可以分組,排序等 criteriaQuery.where(this.predicate); return criteriaQuery.getRestriction();查詢
基礎(chǔ)工作完成了,我們終于可以使用我們的謂語進(jìn)行查詢啦。
/** * 查詢符合條件的考評(píng)員信息 * @param district 區(qū)域 * @param department 部門 * @param discipline 學(xué)科類別 * @return 符合條件的考評(píng)員信息 */ @Override public ListgetAllExaminerInfo(District district, Department department, Discipline discipline) { logger.debug("新建Map并設(shè)置屬性"); Map map = new HashMap<>(); map.put("department", department); map.put("district", district); map.put("discipline", discipline); logger.debug("調(diào)用根據(jù)Map的查詢方法"); return this.getExaminerInfoByMap(map); } /** * 根據(jù)Map查詢考評(píng)員的信息 * @param map 考評(píng)員查詢Map * @return 考評(píng)員列表 */ private List getExaminerInfoByMap(Map map) { logger.debug("獲取當(dāng)前登錄用戶"); User currentLoginUser = userService.getCurrentLoginUser(); logger.debug("獲取查詢謂語"); Specification specification = ExaminerSpecs.base(currentLoginUser.getDepartment(), map); logger.debug("查詢并返回"); return (List ) examinerRepository.findAll(specification); }
怎么樣,謂語拼接完之后是不是很簡(jiǎn)單呢?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69615.html
摘要:以前都是用進(jìn)行數(shù)據(jù)庫的開發(fā),最近學(xué)習(xí)之后發(fā)現(xiàn)顯得更友好,所以我們就一起來了解一下的原理吧。簡(jiǎn)單介紹持久性是的一個(gè)規(guī)范。它用于在對(duì)象和關(guān)系數(shù)據(jù)庫之間保存數(shù)據(jù)。充當(dāng)面向?qū)ο蟮念I(lǐng)域模型和關(guān)系數(shù)據(jù)庫系統(tǒng)之間的橋梁。是標(biāo)識(shí)出主鍵是指定主鍵的自增方式。 以前都是用Mybatis進(jìn)行數(shù)據(jù)庫的開發(fā),最近學(xué)習(xí)Spring Boot之后發(fā)現(xiàn)JPA顯得更友好,所以我們就一起來了解一下JPA的原理吧。 Spr...
摘要:與的關(guān)系是什么是官方提出的持久化規(guī)范。它為開發(fā)人員提供了一種對(duì)象關(guān)聯(lián)映射工具來管理應(yīng)用中的關(guān)系數(shù)據(jù)。他的出現(xiàn)主要是為了簡(jiǎn)化現(xiàn)有的持久化開發(fā)工作和整合技術(shù),結(jié)束現(xiàn)在,,等框架各自為營的局面。定義了在對(duì)數(shù)據(jù)庫中的對(duì)象處理查詢和事務(wù)運(yùn)行時(shí)的的。 導(dǎo)讀: 在上篇文章中對(duì)Spring MVC常用的一些注解做了簡(jiǎn)要的說明,在這篇文章中主要對(duì)Spring Data JPA 做一個(gè)簡(jiǎn)要的說明,并附有一...
摘要:要是緊急排查個(gè)問題,媽蛋雖然有很多好處,比如和底層的無關(guān)。你的公司如果有,是不允許你亂用的。 知乎看到問題《SpringBoot開發(fā)使用Mybatis還是Spring Data JPA??》,順手一答,討論激烈。我實(shí)在搞不懂spring data jpa為啥選了hibernate作為它的實(shí)現(xiàn),是Gavin King的裙帶關(guān)系么?DAO層搞來搞去,從jdbc到hibernate,從top...
摘要:忽略該字段的映射省略創(chuàng)建數(shù)據(jù)訪問層接口,需要繼承,第一個(gè)泛型參數(shù)是實(shí)體對(duì)象的名稱,第二個(gè)是主鍵類型。 SpringBoot 是為了簡(jiǎn)化 Spring 應(yīng)用的創(chuàng)建、運(yùn)行、調(diào)試、部署等一系列問題而誕生的產(chǎn)物,自動(dòng)裝配的特性讓我們可以更好的關(guān)注業(yè)務(wù)本身而不是外部的XML配置,我們只需遵循規(guī)范,引入相關(guān)的依賴就可以輕易的搭建出一個(gè) WEB 工程 上一篇介紹了Spring JdbcTempl...
閱讀 2933·2021-11-23 09:51
閱讀 1587·2021-11-15 11:36
閱讀 3047·2021-10-13 09:40
閱讀 2022·2021-09-28 09:35
閱讀 13172·2021-09-22 15:00
閱讀 1398·2019-08-29 13:56
閱讀 2955·2019-08-29 13:04
閱讀 2725·2019-08-28 18:06