JPA如何使用nativequery多表關(guān)聯(lián)查詢返回自定義實(shí)體類
JPA nativequery多表關(guān)聯(lián)查詢返回自定義實(shí)體類
JPA官方推薦的多表關(guān)聯(lián)查詢使用不便,接觸的有些項(xiàng)目可能會(huì)使用JPA 做簡(jiǎn)單查詢,Mybaits做復(fù)雜查詢。所以想要尋找一種好用的解決方案。
JPA多表關(guān)聯(lián)的實(shí)現(xiàn)方式
1.使用Specification實(shí)現(xiàn)映射關(guān)系匹配,如@ManyToOne等
2.使用NativeQuery等sql或hql來(lái)實(shí)現(xiàn)
優(yōu)缺點(diǎn)對(duì)比
1.映射關(guān)系是hibernate的入門基礎(chǔ),很多人都會(huì)習(xí)慣去使用。個(gè)人不太喜歡這種方式,復(fù)用性太弱,且不靈活特別是在多表復(fù)雜業(yè)務(wù)情況下。
2.使用Specification方式需要繼承JpaSpecificationExecutor接口,構(gòu)造對(duì)應(yīng)的方法后傳入封裝查詢條件的Specification對(duì)象。邏輯上簡(jiǎn)單易懂,但是構(gòu)造Specification對(duì)象需要拼接格式條件非常繁瑣。
3.直接使用NativeQuery等方式實(shí)現(xiàn)復(fù)雜查詢個(gè)人比較喜歡,直觀且便利,弊端在于無(wú)法返回自定義實(shí)體類。需要手動(dòng)封裝工具類來(lái)實(shí)現(xiàn)Object到目標(biāo)對(duì)象的反射。
使用sql并返回自定義實(shí)體類
個(gè)人比較喜歡的實(shí)現(xiàn)方式,不多說(shuō)看代碼
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
@Repository
public class EntityManagerDAO {
@PersistenceContext
private EntityManager entityManager;
/**
* 人員列表排序
* @return
*/
@Transactional
public List<BackstageUserListDTO> listUser(){
String sql = "select a.create_time createTime," +
"a.mobilephone phoneNum," +
"a.email email,a.uid uid," +
"a.enabled enabled," +
"c.id_number idNumber," +
" (case b.`status` when 1 then 1 else 0 end) status " +
"from tbl_sys_user a " +
"LEFT JOIN user_high_qic b on a.uid=b.u_id" +
"LEFT JOIN user_qic c on a.uid=c.uid " +
"ORDER BY status desc";
SQLQuery sqlQuery = entityManager.createNativeQuery(sql).unwrap(SQLQuery.class);
Query query =
sqlQuery.setResultTransformer(Transformers.aliasToBean(BackstageUserListDTO.class));
List<BackstageUserListDTO> list = query.list();
entityManager.clear();
return list;
}
}
public class BackstageUserListDTO implements Serializable{
private static final long serid = 1L;
private String createTime;
private String phoneNum;
private String email;
private BigInteger uid;
private Integer enabled;
private String idNumber;
private BigInteger status;
//GETTER SETTER
}
這樣一個(gè)需求如果使用前兩種方式實(shí)現(xiàn),無(wú)疑會(huì)非常麻煩。使用這種方式能夠直接反射需要的自定義實(shí)體類。
可以根據(jù)需求整理封裝成不同的方法,加入排序,分頁(yè)等。我在這里主要提供一種方便的解決思路。
JPA多表關(guān)聯(lián)動(dòng)態(tài)查詢(自定義sql語(yǔ)句)
項(xiàng)目需求,查詢需求數(shù)據(jù)需要多表鏈接——>根據(jù)多種條件篩選查詢到的數(shù)據(jù),在網(wǎng)上查了很多資料最終選擇這個(gè)字符串拼接查詢
類似如此動(dòng)態(tài)查詢
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-cA0h3Qnw-1590984326742)(../AppData/Roaming/Typora/typora-user-images/image-20200529152434229.png)]](http://img.jbzj.com/file_images/article/202111/2021111811473623.png)
以下是本人項(xiàng)目中使用總結(jié):
實(shí)體類
/**
* 訂單表
*/
@Entity
@Table(name = "signedorder")
@Getter
@Setter
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class SignedOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;//id
@CreatedDate
@Column(updatable = false)
private Date createTime;//創(chuàng)建時(shí)間
@LastModifiedDate
@Column
private Date lastModifiedTime;//修改時(shí)間
@ManyToOne(fetch = FetchType.EAGER, cascade = {
CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH
})
@JoinTable(name = "staff_signedorder", joinColumns = @JoinColumn(name =
"signedorder_id"), inverseJoinColumns = @JoinColumn(name = "staff_id"))
private Staff staff;//所屬用戶
@JoinColumn(name = "industry_id")
private Integer industryId;//行業(yè)Id
}
/**
* 用戶表
*/
@Entity
@Table(name = "staff")
@Getter
@Setter
@NoArgsConstructor
public class Staff {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;//id
@Column(name = "name", length = 25)
private String name;//姓名
@JoinColumn(name = "city_id")
private Integer cityId;//城市id
/**
* 城市表
*/
@Entity
@Table(name = "city")
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
public class City {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;//id
@Column(name = "name", length = 50)
private String name;//名稱
}
//行業(yè)表和城市表一致,就不展示了
注解解釋
實(shí)體類中相關(guān)注解解釋:
@Entity: 對(duì)實(shí)體注釋。任何Hibernate映射對(duì)象都要有這個(gè)注釋@Table: 聲明此對(duì)象映射到數(shù)據(jù)庫(kù)的數(shù)據(jù)表,該注釋不是必須的,如果沒有則系統(tǒng)使用默認(rèn)值(實(shí)體的短類名)@Getter、@Setter、@NoArgsConstructor:lombok提供注解,get、set方法及無(wú)參構(gòu)造@EntityListeners(AuditingEntityListener.class):加上此注解,時(shí)間注解@LastModifiedDate 和 @CreatedDate才可以生效@Id: 聲明此屬性為主鍵@GeneratedValue(strategy = GenerationType.IDENTITY):指定主鍵,
TABLE:使用一個(gè)特定的數(shù)據(jù)庫(kù)表格來(lái)保存主鍵;
IDENTITY:主鍵由數(shù)據(jù)庫(kù)自動(dòng)生成(主要是自動(dòng)增長(zhǎng)型);
SEQUENCR:根據(jù)底層數(shù)據(jù)庫(kù)的序列來(lái)生成主鍵,條件是數(shù)據(jù)庫(kù)支持序列;
AUTO:主鍵由程序控制
@CreatedDate(updatable = false):創(chuàng)建時(shí)間時(shí)間字段,在insert的時(shí)候,會(huì)設(shè)置值;update時(shí)時(shí)間不變@LastModifiedDate:修改時(shí)間段,update時(shí)會(huì)修改值@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}): 多對(duì)一,
FetchType.EAGER:立即加載, 獲取關(guān)聯(lián)實(shí)體;
CascadeType.MERGE: 級(jí)聯(lián)更新;
CascadeType.PERSIST:級(jí)聯(lián)新建;
CascadeType.REFRESH:級(jí)聯(lián)刷新
@JoinTable: JoinColumn:保存關(guān)聯(lián)關(guān)系的外鍵的字段;inverseJoinColumns:保存關(guān)系的另外一個(gè)外鍵字@Column:用來(lái)標(biāo)識(shí)實(shí)體類中屬性與數(shù)據(jù)表中字段的對(duì)應(yīng)關(guān)系
測(cè)試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class SprinBootMarketingsystemApplicationTests {
@PersistenceContext//jpa的數(shù)據(jù)庫(kù)操作類
private EntityManager entityManger;
@Test
public void queryDb(){
//給參數(shù)賦值
Integer cityId = 1;
Integer industryId = 2;
Integer staffId = 16;
Date startTime = DateUtil.parse("1970-01-01");//字符串時(shí)間轉(zhuǎn)換為date類型
Date endTime = Calendar.getInstance().getTime();//獲取系統(tǒng)當(dāng)前時(shí)間裝換為date類型
//創(chuàng)建SQL語(yǔ)句主體
StringBuffer stringBuffer = new StringBuffer("\tSELECT\n" +
"\tcount( * ) count,\n" +
"\tci.NAME cityName\n" +
"\tFROM\n" +
"\tsignedorder s\n" +
"\tLEFT JOIN staff_signedorder t ON s.id = t.signedorder_id\n" +
"\tLEFT JOIN staff sta ON t.staff_id = sta.id\n" +
"\tLEFT JOIN city ci ON sta.city_id = ci.id\n" +
"\tWHERE\n" +
"\t1 = 1");
Map<String,Object> map = new HashMap<>();
//拼接動(dòng)態(tài)參數(shù)
if(industryId != null){
/*第一種給參數(shù)賦值方式
1代表傳進(jìn)來(lái)的參數(shù)順序,給參數(shù)賦值nativeQuery.setParameter(1, industryId);
stringBuffer.append(" and s.industryId = ?1");*/
//industryId代表傳進(jìn)來(lái)的參數(shù)名稱,給參數(shù)賦值nativeQuery.setParameter("industryId", industryId);
stringBuffer.append(" and s.industry_id = :industryId");
map.put("industryId",industryId);
}
if(cityId != null){
stringBuffer.append(" and ci.id = :cityId");
map.put("cityId",cityId);
}
if(staffId != null){
stringBuffer.append(" and sta.id = :staffId");
map.put("staffId",staffId);
}
if(startTime!=null && endTime!=null){
//使用這種賦值方式,時(shí)間類型需要給三個(gè)參數(shù),參數(shù)名稱,參數(shù)值,特定映射的類型TemporalType.DATE
//nativeQuery.setParameter("create_time", startTime,TemporalType.DATE);
stringBuffer.append( " and s.create_time BETWEEN :startTime and :endTime ");
map.put("startTime",startTime);
map.put("endTime",endTime);
}
Query nativeQuery = entityManger.createNativeQuery(stringBuffer.toString());
for (String key : map.keySet()) {
nativeQuery.setParameter(key, map.get(key));
}
//三種接受返回結(jié)果方式(第一種方式)
/*nativeQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.TO_LIST);
List resultList1 = nativeQuery.getResultList();
for (Object o : resultList1) {
System.out.println(o.toString());
}*/
//第二種方式和第一種方式相似
/*nativeQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String, Object>> resultList = nativeQuery.getResultList();
for (Map<String, Object> map1 :resultList
) {
System.out.println(map1);
}*/
//第三種方式:實(shí)體類接受
nativeQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(TestVo.class));
List<TestVo> resultList = nativeQuery.getResultList();
for (TestVo svo:resultList
) {
System.out.println(svo.toString());
}
}
打印結(jié)果
第一種方式打印結(jié)果
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-iB3CVBpD-1590984326744)(ceshi1.png)]](http://img.jbzj.com/file_images/article/202111/2021111811473624.png)
第二種方式打印結(jié)果
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-lHOgQW55-1590984326745)(ceshi2.png)]](http://img.jbzj.com/file_images/article/202111/2021111811473625.png)
第三種方式打印結(jié)果
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-JTMsc780-1590984326747)(ceshi3.png)]](http://img.jbzj.com/file_images/article/202111/2021111811473626.png)
TestVo實(shí)體接收類
@Data
public class TestVo {
private String cityName;//城市名字
private BigInteger count;//簽單數(shù)量(必須使用BigInteger類型接受)
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JAVA隨機(jī)數(shù)隨機(jī)字母的實(shí)現(xiàn)(微信搶紅包小練習(xí))
這篇文章主要介紹了JAVA隨機(jī)數(shù)隨機(jī)字母的實(shí)現(xiàn)(微信搶紅包小練習(xí)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Spring Boot Debug調(diào)試過(guò)程圖解
這篇文章主要介紹了Spring Boot Debug調(diào)試過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
java 中RSA的方式實(shí)現(xiàn)非對(duì)稱加密的實(shí)例
這篇文章主要介紹了java 中RSA的方式實(shí)現(xiàn)非對(duì)稱加密的實(shí)例的相關(guān)資料,這里提供實(shí)例幫助大家理解這部分知識(shí),需要的朋友可以參考下2017-08-08
Java通過(guò)匿名類來(lái)實(shí)現(xiàn)回調(diào)函數(shù)實(shí)例總結(jié)
這篇文章主要介紹了Java通過(guò)匿名類來(lái)實(shí)現(xiàn)回調(diào)函數(shù)的例子,回調(diào)函數(shù)就是一種函數(shù)簽名(若干個(gè)輸入?yún)?shù)、一個(gè)輸出參數(shù))的規(guī)范,java雖不存在函數(shù)聲明,但是java可以用接口來(lái)強(qiáng)制規(guī)范。具體操作步驟大家可查看下文的詳細(xì)講解,感興趣的小伙伴們可以參考一下。2017-08-08
SpringCloud配置動(dòng)態(tài)更新原理解析
在微服務(wù)架構(gòu)的浩瀚星海中,服務(wù)配置的動(dòng)態(tài)更新如同魔法一般,能夠讓應(yīng)用在不重啟的情況下,實(shí)時(shí)響應(yīng)配置的變更,Spring Cloud作為微服務(wù)架構(gòu)中的佼佼者,其動(dòng)態(tài)配置更新的能力尤為引人注目,本文給大家介紹了SpringCloud配置動(dòng)態(tài)更新原理,需要的朋友可以參考下2025-01-01
Mybatis-Plus的SQL語(yǔ)句組拼原理說(shuō)明
這篇文章主要介紹了Mybatis-Plus的SQL語(yǔ)句組拼原理說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06

