在這周對(duì)代碼進(jìn)行重構(gòu)時(shí)秦踪,發(fā)現(xiàn)了一個(gè)很隱晦的bug捌肴,我認(rèn)為很有必要把它記下來式矫。產(chǎn)生bug詳細(xì)流程如下:
- 首先我是采用JPA提供的執(zhí)行原生sql語句的方式去查詢一個(gè)ID狸驳,這個(gè)ID在數(shù)據(jù)庫的類型為bigint预明。查詢方式如下:
@Query(nativeQuery = true,
value = "SELECT car_id FROM cars WHERE car_id IN (?1)")
List<Long> findCarIdsByCars(List<Long> carIds);
調(diào)用方式:
public List<Long> getCarIdByCars(List<Long> vehicleIds) {
List<Long> list = quotationRepository.findCarIdsByCars(vehicleIds);
return longList;
}
這個(gè)查詢的目的是返回?cái)?shù)據(jù)庫已存在的指定ID。從SQL語句看是沒有任何問題的耙箍,但是當(dāng)我通過debug調(diào)用這個(gè)repository會(huì)發(fā)現(xiàn)如下內(nèi)容:
顯然撰糠,List的值竟然是BigInteger的,而我們上面寫的明明是
List<Long>
辩昆,而且這種時(shí)候編譯器也是沒有報(bào)錯(cuò)的阅酪,明明對(duì)象的類型就是不對(duì)的!
尋找原因
通過google搜索發(fā)現(xiàn)汁针,JPA內(nèi)部查詢是通過createSQLQuery和createQuery實(shí)現(xiàn)的术辐。所有的查詢都會(huì)調(diào)用這兩個(gè)方法。它們的不同點(diǎn)是:
- createQuery用的JPQL語句進(jìn)行查詢施无,createSQLQuery用sql語句查詢辉词;
- 前者以hibernate生成的Bean為對(duì)象裝入list返回;后者則是以對(duì)象數(shù)組進(jìn)行存儲(chǔ)帆精;
- 當(dāng)通過createSQLQuery查詢時(shí)较屿,如果存在Bean的實(shí)例,則它會(huì)按照實(shí)例去返回卓练,但是當(dāng)實(shí)例不存在時(shí)它會(huì)按照J(rèn)PA給定的默認(rèn)配置去返回映射值隘蝎,而bigint的映射正是BigInteger,所以才會(huì)出現(xiàn)如上情況襟企。
解決辦法
解決辦法有兩種嘱么,一種是修改repository的返回方式為BigInteger,但是這樣的話還需要調(diào)用代碼去轉(zhuǎn)換顽悼,而且可讀性也不太好曼振。所以建議采用第二種解決辦法:
使用JPQL(Java Persistence Query Language)語言去寫返回值。實(shí)現(xiàn)方式如下:
@Query("SELECT c.carId FROM cars c WHERE c.carId IN (?1)")
List<Long> findCarIdsByCars(List<Long> carIds);
這樣寫JPA會(huì)完美m(xù)ap返回值蔚龙,其實(shí)跟寫原生sql是一樣的冰评。另外個(gè)人覺得還是盡量用JPQL語句進(jìn)行數(shù)據(jù)庫操作比較好,因?yàn)镴PA的存在一個(gè)重要的原因就是為了JAVA與數(shù)據(jù)庫之間的解耦木羹,我們通過JPQL語言可以很好地將table與java代碼之間的耦合分離開來甲雅。何樂而不為呢解孙?