這是一個Spring + Angular先後臺分離的項目,目前有一個查看做業列表的功能,而且已經設置了分頁和幾個查詢參數,如圖。數據庫
如今須要增長一個已評閱和未評閱的查詢功能。app
Work實體的屬性以下:ui
id: number; content: string; createTime: Date; item = new Item(); score: number; student = new Student(); updateTime: Date; reviewed: boolean; attachments = new Array<Attachment>();
已評閱對應的屬性是reviewed,是boolean類型。this
目前前臺服務層的代碼:url
// 已經寫了分頁參數、按學生姓名查找做業、按學號查找做業,共5個參數 getAll(params: {page: number, size: number, studentName?: string, studentSno?: string, itemId?: number }): Observable<Page<Work>> { const _params = { page: params.page.toLocaleString(), size: params.size.toLocaleString(), studentName: params.studentName ? params.studentName : null, studentNo: params.studentSno ? params.studentSno : null, itemId: params.itemId ? params.itemId.toLocaleString() : null }; return this.httpClient.get<Page<Work>>(`${this.url}/getAll`, {params: _params}); }
因爲先後臺中間隔着一層Http,而且boolean具備特殊性,因此向後臺傳值比較困難,做爲新手,我首先能想到幾種方法:spa
這個是最簡單的,由於後臺不用動,直接請求整頁的數據,只在前臺顯示未批閱的做業便可。3d
/** * 單選框被用戶點擊時 * @param $event 彈射值 * @param reviewed 評閱狀態碼1默認2已評閱3未評閱 */ onCheckBoxChange($event: Event, reviewed: number) { this.reviewed = reviewed; this.load(); }
那麼問題來了,因爲請求的是一整頁(10條)數據,若是這一頁都是已批閱,那麼這頁直接就是白的...一條數據也沒有。code
雖然不符合操做邏輯,但這個又不算錯誤,系統不會報錯...orm
前臺過濾,失敗。對象
直接在前臺的M層加入參數:
reviewed: params.reviewed ? params.reviewed : null,
那麼問題來了,httpClient只能傳輸String和對象(即便是傳輸對象,也是序列化變成字符串再傳輸的),不能傳輸number、boolean等類型。
對於number,須要轉化成String類型來傳輸,好比:
itemId: params.itemId ? params.itemId.toLocaleString() : null
但對於boolean類型,並無直接轉化成String類型的方法。
所以,直接傳boolean類型的參數的方式,失敗。
除了Http請求,其餘位置繼續用Boolean傳值,到發起請求的時候轉化爲String。
這樣的好處是:只須要在前臺的服務層和後臺的C層增長轉化代碼,其餘位置繼續按照常規的邏輯,直接用Boolean。
在前臺向後臺的傳輸過程當中:
true -> "true" -> true false -> "false" -> false undefined -> 前臺不傳值 -> null
這樣就用三個值表示了「已評閱」「未評閱」「所有」三種狀態。
缺點也比較明顯,布爾值只有true和false,若是判斷寫成:
reviewed: params.reviewed ? 'true' : null,
那麼undefined 和false都按false來處理,都不會傳值。
因此就必須分開處理undefined和false,能夠用一個嵌套判斷來實現:
reviewed: params.reviewed === undefined ? null : params.reviewed ? '1' : '0',
同理,後臺也須要判斷,轉化成Boolean:
Boolean _reviewed = null; if (reviewed != null){ switch (reviewed) { case "1": _reviewed = true;break; case "0": _reviewed = false;break; } }
如今,實現了效果:
轉化爲字符串的方法,成功。
代碼邏輯:
「嘗試三」的寫法,主要有兩個問題:
因爲boolean具備特殊性,只有兩個值,因此能夠不傳輸這個參數,經過調用不一樣的方法,間接傳輸boolean。
現有的後臺C層方法:
/** * 獲取全部做業 * @param pageable 分頁信息 * @return 全部做業 */ @GetMapping("getAll") @JsonView(GetAllJsonView.class) public Page<Work> findAll( @RequestParam(required = false) Long itemId, @RequestParam(required = false) String studentName, @RequestParam(required = false) String studentNo, Pageable pageable) { return this.workService.getAll( itemId, studentName, studentNo, pageable); }
目前有三個參數,若是不想增長參數,就再增長兩個方法,而參數不變:
// 原C層的方法 public Page<Work> findAll(){} // 獲取已評閱的做業 public Page<Work> findAllReviewed(){} // 獲取未評閱的做業 public Page<Work> findAllUnReviewed(){}
寫到這裏你們應該明白了:在前臺向後臺的傳輸過程當中
true -> 請求findAllReviewed() -> true false -> 請求findAllUnreviewed() -> false undefined -> 請求findAll() -> null
因此前臺這麼寫:
getAll(params: {page: number, size: number, studentName?: string, studentSno?: string, itemId?: number, reviewed?: boolean}): Observable<Page<Work>> { // 準備參數 const _params = { page: params.page.toLocaleString(), size: params.size.toLocaleString(), studentName: params.studentName ? params.studentName : null, studentNo: params.studentSno ? params.studentSno : null, itemId: params.itemId ? params.itemId.toLocaleString() : null }; // 判斷,向對應的方法發起請求 switch (params.reviewed) { case true: return this.httpClient.get<Page<Work>>(`${this.url}/getAllReviewed`, {params: _params}); break; case false: return this.httpClient.get<Page<Work>>(`${this.url}/getAllUnReviewed`, {params: _params}); break; } // 默認請求getAll() return this.httpClient.get<Page<Work>>(`${this.url}/getAll`, {params: _params}); }
而後,後臺的三個方法都調用同一個服務層,這時候就能夠按照常規邏輯,傳入Boolean類型了。
代碼邏輯:
經過Http傳輸boolean參數的方法:
做爲新手,我知道這必定不是最優解,若是有更好的方式,歡迎來打臉,哈哈哈。
在實際項目中,除了傳值,還遇到了一個難處理的問題:數據庫的條件查詢(使用JPA)。
後臺的倉庫層WorkRepository功能:
default Page getAll(Item item, String studentName, String studentSno, @NotNull Pageable pageable) { Assert.notNull(pageable, "傳入的Pageable不能爲null"); Specification<Work> specification = WorkSpecs.containingName(studentName) .and(WorkSpecs.startWithNo(studentSno)) .and(WorkSpecs.belongToItem(item)); return this.findAll(specification, pageable); }
其中的
Specification<Work> specification = WorkSpecs.containingName(studentName) .and(WorkSpecs.startWithNo(studentSno)) .and(WorkSpecs.belongToItem(item));
就是查詢條件,它分別調用了三個方法,分別按照姓名、學號、實驗項目來查詢。
查看WorkSpecs類,三個方法的代碼以下:
public class WorkSpecs { public static Specification<Work> belongToItem(Item item) { if (null == item || null == item.getId()) { return Specification.where(null); } return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("item").as(Item.class), item); } public static Specification<Work> containingName(String studentName) { if (studentName != null) { return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.like(root.get("student").get("name").as(String.class), String.format("%%%s%%", studentName)); } else { return Specification.where(null); } } public static Specification<Work> startWithNo(String studentNo) { if (studentNo == null) { return Specification.where(null); } return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.like(root.get("student").get("no").as(String.class), String.format("%s%%", studentNo)); } }
如今增長一個reviewed參數,就要增長相應的查詢條件。
因此第四個條件這樣寫:
public static Specification<Work> isReviewed(Boolean reviewed) { if (reviewed == null) { return Specification.where(null); } return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("reviewed").as(Boolean.class), reviewed); }
在倉庫層getAll()方法增長:
.and(WorkSpecs.isReviewed(reviewed));
就是實現JPA經過Boolean參數查詢數據庫了。