JPA自定義對(duì)象接收查詢結(jié)果集操作
最近使用JPA的時(shí)候,碰到需要自定義查詢結(jié)果集的場(chǎng)景,網(wǎng)上搜了一下,都是需要自定義方法寫一大串代碼實(shí)現(xiàn)的,太繁瑣了,有那時(shí)間還不如用mybaits。
用JPA就是要盡量通過(guò)聲明接口解決持久層問(wèn)題,要不然鬼用。逼得沒(méi)辦法去了官網(wǎng)看看文檔,再?zèng)]有就放棄了,沒(méi)時(shí)間看源碼。最終找到我想要的結(jié)果了。
例如,傳統(tǒng)的JPA接口實(shí)現(xiàn)如下所示:
class Person { @Id UUID id; String firstname, lastname; Address address; static class Address { String zipCode, city, street; } } interface PersonRepository extends Repository<Person, UUID> { Collection<Person> findByLastname(String lastname); }
自定義對(duì)象接收查詢結(jié)果集方法如下:
(1)增加接收數(shù)據(jù)接口
interface NamesOnly { String getFirstname(); String getLastname(); }
(2)增加持久層接口
interface PersonRepository extends Repository<Person, UUID> { Collection<NamesOnly> findByLastname(String lastname); }
如果要對(duì)查詢結(jié)果進(jìn)行序列號(hào)的話就會(huì)有點(diǎn)問(wèn)題:
{ "errorCode": "00", "errorMessage": "操作成功", "returnObject": [ { "createtime": 1526358195000, "id": 49, "lastupdatetime": 1526358195000, "status": "2", "target": { "createtime": 1526358195000, "lastupdatetime": 1526358195000, "check_Wicket": "1", "facility_name": "血壓測(cè)量", "facility_Num": "C3", "id": 49, "status": "2", "check_name": "小湯154", "check_Num": "BY185201805140001" }, "targetClass": "org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap" } ] }
會(huì)出現(xiàn)targetClass這個(gè)字段,不能直接把結(jié)果拿來(lái)用,很惡心,又不想寫代碼中轉(zhuǎn)下。
經(jīng)過(guò)后來(lái)的摸索,其實(shí)如果只是為了返回JSON,也可以直接在Repository層直接用List<Map<String,Object>>來(lái)返回,
Map<String,Object>對(duì)應(yīng)單條查詢結(jié)果,完美解決序列化問(wèn)題。
完畢。就這么簡(jiǎn)單。
補(bǔ)充:SpringBoot JPA查詢結(jié)果映射到自定義實(shí)體類
場(chǎng)景
舉一個(gè)簡(jiǎn)單的例子:
比如有一個(gè)Position實(shí)體類
@Entity @Table(name = "position") public class Position implements Serializable { private static final long serialVersionUID = 768016840645708589L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private BigDecimal salary; private String city; ...//省略getset方法 ...//省略toString方法 }
然后有一個(gè)PositionDetail實(shí)體類
@Entity @Table(name = "position_detail") public class PositionDetail implements Serializable { private static final long serialVersionUID = 4556211410083251360L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long pid; private String description; ...//省略getset方法 ...//省略toString方法 }
需求:
查詢職位基本信息,職位描述,因?yàn)樯婕暗絻蓮埍聿僮?,?jiǎn)單的查詢并不能滿足我們的需求,因此就需要自定義查詢接口并返回符合需求的結(jié)果。
接下來(lái)再定義一個(gè)實(shí)體類,用來(lái)接收查詢結(jié)果
public class PositionDO { private Long id; private String name; private BigDecimal salary; private String city; private String description; public PositionDO(Long id, String name, BigDecimal salary, String city, String description) { this.id = id; this.name = name; this.salary = salary; this.city = city; this.description = description; } ...//省略getset方法 ...//省略toString方法 }
編寫Dao接口,用來(lái)實(shí)現(xiàn)CRUD操作
public interface PositionDao extends JpaRepository<Position, Long> { @Query(nativeQuery = true, value = "select t1.id, t1.name, t1.salary, t1.city, t2.description) \n" + "from position t1 left join position_detail t2 on t1.id = t2.pid \n" + "where t1.id = :id") PositionDO findPositionById(@Param("id") Long id); }
思考:
如果這樣寫會(huì)不會(huì)出現(xiàn)問(wèn)題?接下來(lái)我們編寫一個(gè)測(cè)試類測(cè)試一下。
@SpringBootTest(classes = ShardingApplication.class) @RunWith(SpringRunner.class) public class TestShardingDatabase { @Resource PositionDao positionDao; @Test public void testQueryById() throws Exception{ PositionDO positionDO = positionDao.findPositionById(548083053407240192L); System.out.println(positionDO); } }
哈哈,翻車了吧,還好先測(cè)試了一波,問(wèn)題不大。
Hibernate: select t1.id, t1.name, t1.salary, t1.city, t2.description from position t1 left join position_detail t2 on t1.id = t2.pid where t1.id = ? org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.lsp.dto.PositionDO]
分析原因:
那么到底是為什么造成這樣的原因呢?相信小伙伴們一眼就能看出來(lái)了,是因?yàn)镴pa找不到能夠從類型轉(zhuǎn)換的轉(zhuǎn)換器,而拋出這樣的異常。
現(xiàn)在問(wèn)題來(lái)了,既然這樣不行,那么我們想要實(shí)現(xiàn)映射到自定義結(jié)果集該如何實(shí)現(xiàn)呢?
實(shí)現(xiàn):
JPA可以自定義SQL語(yǔ)句進(jìn)行查詢,然后查詢語(yǔ)句可以通過(guò)原生SQL語(yǔ)句(原生SQL語(yǔ)句也就是在@Query注解里加上nativeQuery = true)進(jìn)行查詢。當(dāng)然了,也可以通過(guò)JPQL進(jìn)行查詢。
我們這里就是通過(guò)JPQL進(jìn)行查詢,它的特征就是與原生SQL語(yǔ)句類似,完全面向?qū)ο?,通過(guò)類名和屬性訪問(wèn),而不是表名和表屬性。
由此PositionDao修改之后就像這樣
public interface PositionDao extends JpaRepository<Position, Long> { @Query(value = "select new com.lsp.domain.PositionDO(t1.id, t1.name, t1.salary, t1.city, t2.description) \n" + "from Position t1 left join PositionDetail t2 on t1.id = t2.pid \n" + "where t1.id = :id") PositionDO findPositionById(@Param("id") Long id); }
接下來(lái)我們?cè)龠\(yùn)行測(cè)試方法看一下。
Hibernate: select position0_.id as col_0_0_, position0_.name as col_1_0_, position0_.salary as col_2_0_, position0_.city as col_3_0_, positionde1_.description as col_4_0_ from position position0_ left outer join position_detail positionde1_ on (position0_.id=positionde1_.pid) where position0_.id=? PositionDO{id=548083053407240192, name='Jerry5', salary=10000.00, city='beijing5', description='this is message 5'}
ok了,結(jié)果已經(jīng)正確查詢出來(lái)。
總結(jié):
注意上面的SQL語(yǔ)句是面向?qū)ο蟮?,?duì)應(yīng)的字段也都是實(shí)體類里面的屬性。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
基于Spring MVC的文件上傳和下載實(shí)現(xiàn)方法
在Web應(yīng)用程序中,文件上傳和下載是常見(jiàn)的功能,Spring MVC框架提供了方便的方式來(lái)實(shí)現(xiàn)這些功能,本文將介紹如何使用Spring MVC實(shí)現(xiàn)文件上傳和下載,需要的朋友可以參考下2023-05-05JavaWeb中使用JavaMail實(shí)現(xiàn)發(fā)送郵件功能實(shí)例詳解
這篇文章主要介紹了JavaWeb中使用JavaMail實(shí)現(xiàn)發(fā)送郵件功能的實(shí)例代碼,非常不錯(cuò)具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-05-05Java使用貪心算法解決電臺(tái)覆蓋問(wèn)題(示例詳解)
貪心算法是指在對(duì)問(wèn)題進(jìn)行求解時(shí),在每一步選擇中都采取最好或最優(yōu)的選擇,從而導(dǎo)致結(jié)果理想化,下面通過(guò)本文介紹下Java使用貪心算法解決電臺(tái)覆蓋問(wèn)題,需要的朋友可以參考下2022-04-04Java開(kāi)發(fā)者結(jié)合Node.js編程入門教程
這篇文章主要介紹了Java開(kāi)發(fā)者結(jié)合Node.js編程入門教程,我將先向您展示如何使用Java EE創(chuàng)建一個(gè)簡(jiǎn)單的Rest服務(wù)來(lái)讀取 MongoDB數(shù)據(jù)庫(kù)。然后我會(huì)用node.js來(lái)實(shí)現(xiàn)相同的功能,需要的朋友可以參考下2014-09-09Java 內(nèi)置Http Server構(gòu)建web應(yīng)用案例詳解
這篇文章主要介紹了Java 內(nèi)置Http Server構(gòu)建web應(yīng)用案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09springBoot項(xiàng)目中的static和templates文件夾的使用
本文主要介紹了springBoot項(xiàng)目中的static和templates文件夾的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07FuncGPT慧函數(shù)保護(hù)數(shù)據(jù)安全提高代碼質(zhì)量減少軟件故障(java示例)
這篇文章主要為大家介紹了FuncGPT慧函數(shù)保護(hù)數(shù)據(jù)安全提高代碼質(zhì)量減少軟件故障(java示例),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10解讀Java中打印輸出對(duì)象內(nèi)容為什么可以不寫.toString()
這篇文章主要介紹了解讀Java中打印輸出對(duì)象內(nèi)容為什么可以不寫.toString()問(wèn)題,具有很的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09