5. JPA物件繼承關係
在實體建模過程中,有些實體會有多種變形,其中大部分的屬性都是共用的,只有一小部分是特有的。這時較優雅的設計是將共用的屬性抽象出來形成基類,實現類再去擴充套件特有屬性。領域服務可將通用服務抽象出來形成基類服務,再擴充套件特有服務。而Repository設計,一般情況也是先抽象基礎,再擴充套件特有方法,呼叫時一般提供泛型支援,根據實現類的型別呼叫具體的Repository。 今天介紹使用@Inheritance註解讓一個Repository支援所有實現類,從而簡化Repository的設計。
一、物件建模
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "member_type")
public class Member {
@Id
private String memberCode;
private String memberName;
}
@DiscriminatorValue("store")
public class StoreMember extends Member {
private String memberCard;
private Integer memberLevel;
}
@DiscriminatorValue("wexin")
public class WeXinMember extends Member{
private String openId;
private String nickName;
}
@Inheritance用來配置父類
InheritanceType.SINGLE_TABLE 將所有實現類的所有欄位對映到一個表裡。
InheritanceType.TABLE_PER_CLASS 將每個實現類合併基類的欄位對映到單獨的表裡,每個表相關獨立且沒有關聯。
InheritanceType.JOINED 將基類和每個實現類分別對映到獨立的表裡,並使用主鍵進行關聯,實現類只包含自己獨有的欄位。
@DiscriminatorColumn(name = "member_type") 用來做實現類區別標識的欄位,如果不指定name,則會自動新建dtype欄位。此欄位系統會自動賦值,不需要人為指定,且不能作為屬性存在。
@DiscriminatorValue("wexin") 實現類區別的標識值,jpa會根據具體標識值將資料持久化到對應的表中,查詢語句也可自動識別型別
二、Repository設計
1. 查詢
@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
WeXinMember findFirstByNickName(String openId);
WeXinMember findFirstByMemberCode(String memberCode);
StoreMember findTop1ByMemberCode(String memberCode);
Member findFirstByMemberName(String memberName);
}
從上面可以看到,所有的實現類都可視為一個整體,可直接使用某個子類的屬性做查詢條件,可以有子類的返回值,也可以設定基類返回值。
如果返回值為基類Member,jpa則返回Hibernate的代理類,需要Hibernate.unproxy(member)才能得到具體的實現類
Member member = memberRepository.findFirstByMemberName("微信會員");
if (Hibernate.unproxy(member) instanceof WeXinMember) {
WeXinMember weXinMember = (WeXinMember) (Hibernate.unproxy(member));
System.err.println(weXinMember);
}
如果返回值為實現類則可以直接使用
WeXinMember weXinMember = memberRepository.findFirstByMemberCode("W001");
2. 寫入和刪除
寫入和刪除操作,jpa都視為一個整體,可以直接使用memberRepository預設的方法
WeXinMember weXinMember = new WeXinMember();
weXinMember.setMemberCode("W001");
weXinMember.setMemberName("微信會員");
weXinMember.setMemberType("wexin");
weXinMember.setNickName("twoDog");
weXinMember.setOpenId(UUID.randomUUID().toString());
memberRepository.save(weXinMember);
...
memberRepository.save(weXinMember);
...
memberRepository.delete(weXinMember);
三、Repository查詢語句分析
使用哪種方式構建,主要考慮資料庫的表結構關係
1.InheritanceType.SINGLE_TABLE 構建單表模式
不同的返回值型別,SQL語句有差別
**基類:**將所有欄位都查詢出來,有不必要的效能開銷
Member findFirstByMemberCode(String memberCode);
SELECT
member0_.member_code AS member_c2_0_0_,
member0_.member_name AS member_n3_0_0_,
member0_.member_card AS member_c4_0_0_,
member0_.member_level AS member_l5_0_0_,
member0_.nick_name AS nick_nam6_0_0_,
member0_.open_id AS open_id7_0_0_,
member0_.member_type AS member_t1_0_0_
FROM
member member0_
WHERE
member0_.member_code = 'W001'
**實現類:**只查詢實現類的欄位
WeXinMember findFirstByMemberCode(String memberCode);
SELECT
wexinmembe0_.member_code AS member_c2_0_,
wexinmembe0_.member_name AS member_n3_0_,
wexinmembe0_.nick_name AS nick_nam6_0_,
wexinmembe0_.open_id AS open_id7_0_
FROM
member wexinmembe0_
WHERE
wexinmembe0_.member_type = 'wexin'
AND wexinmembe0_.member_code = 'W001'
2.InheritanceType.TABLE_PER_CLASS構建獨立表模式
基類:將所有子表都union進來再查詢,此方法不可取,效能開銷大
Member findFirstByMemberCode(String memberCode);
SELECT
member0_.member_code AS member_c1_0_0_,
member0_.member_name AS member_n2_0_0_,
member0_.member_card AS member_c1_1_0_,
member0_.member_level AS member_l2_1_0_,
member0_.nick_name AS nick_nam1_2_0_,
member0_.open_id AS open_id2_2_0_,
member0_.clazz_ AS clazz_0_
FROM
(SELECT
member_code,
member_name,
NULL AS member_card,
NULL AS member_level,
NULL AS nick_name,
NULL AS open_id,
0 AS clazz_
FROM
member UNION ALL SELECT
member_code,
member_name,
member_card,
member_level,
NULL AS nick_name,
NULL AS open_id,
1 AS clazz_
FROM
store_member UNION ALL SELECT
member_code,
member_name,
NULL AS member_card,
NULL AS member_level,
nick_name,
open_id,
2 AS clazz_
FROM
we_xin_member) member0_
WHERE
member0_.member_code = 'W001'
**實現類:**只查詢實現類的欄位
WeXinMember findFirstByMemberCode(String memberCode);
SELECT
wexinmembe0_.member_code AS member_c1_0_,
wexinmembe0_.member_name AS member_n2_0_,
wexinmembe0_.nick_name AS nick_nam1_2_,
wexinmembe0_.open_id AS open_id2_2_
FROM
we_xin_member wexinmembe0_
WHERE
wexinmembe0_.member_code = 'W001'
3. InheritanceType.JOINED構建關聯表模式
**基類:**將所有子表進行關聯後再查詢,子表多了效能開銷大
Member findFirstByMemberCode(String memberCode);
SELECT
member0_.member_code AS member_c2_0_0_,
member0_.member_name AS member_n3_0_0_,
member0_1_.member_card AS member_c1_1_0_,
member0_1_.member_level AS member_l2_1_0_,
member0_2_.nick_name AS nick_nam1_2_0_,
member0_2_.open_id AS open_id2_2_0_,
member0_.member_type AS member_t1_0_0_
FROM
member member0_
LEFT OUTER JOIN
store_member member0_1_ ON member0_.member_code = member0_1_.member_code
LEFT OUTER JOIN
we_xin_member member0_2_ ON member0_.member_code = member0_2_.member_code
WHERE
member0_.member_code = 'W001'
**實現類:**關聯主表和當前型別子表查詢
WeXinMember findFirstByMemberCode(String memberCode);
SELECT
wexinmembe0_.member_code AS member_c2_0_,
wexinmembe0_1_.member_name AS member_n3_0_,
wexinmembe0_.nick_name AS nick_nam1_2_,
wexinmembe0_.open_id AS open_id2_2_
FROM
we_xin_member wexinmembe0_
INNER JOIN
member wexinmembe0_1_ ON wexinmembe0_.member_code = wexinmembe0_1_.member_code
WHERE
wexinmembe0_.member_code = 'W001'
綜上:如果有明確的型別時,查詢方法的返回值應該設定為具體現實類,以便於優化查詢語句
四、適用場景
此功能還是挺新奇的,適用於包含多種變形類操作的場景,此方法比直接使用泛型處理更方便,更容易處理資料,但需要關注資料庫結構與查詢語句的效能影響,建議使用InheritanceType.JOINED模式
五. 原始碼
https://gitee.com/hypier/barry-jpa/tree/master/jpa-section-4
相關文章
- ArrayList繼承關係分析繼承
- #JAVA#物件導向(繼承中成員方法的關係)Java物件繼承
- 繼承關係和魔術方法繼承
- c++中的繼承關係C++繼承
- Flutter 吐血整理元件繼承關係圖Flutter元件繼承
- C++ exception 異常類繼承關係C++Exception繼承
- java繼承關係下執行順序Java繼承
- 物件-原型-繼承物件原型繼承
- Flutter深入淺出元件篇---繼承關係圖Flutter元件繼承
- 物件導向:繼承物件繼承
- 物件導向-繼承物件繼承
- JavaScript物件冒充繼承JavaScript物件繼承
- 物件導向--繼承物件繼承
- JavaScript之物件繼承JavaScript物件繼承
- 關於物件繼承的問題——利用空物件做中介物件繼承
- JAVA類的頂層Type抽象和繼承關係Java抽象繼承
- 深入Spring Boot:ClassLoader的繼承關係和影響Spring Boot繼承
- java物件導向繼承Java物件繼承
- Golang物件導向_繼承Golang物件繼承
- 物件導向之繼承物件繼承
- Java面試題:Java中的集合及其繼承關係Java面試題繼承
- 深刻理解php“繼承“、“私有屬性“、“$this指向“的關係PHP繼承
- 物件、原型鏈、類、繼承【上】物件原型繼承
- 講清楚之 javascript 物件繼承JavaScript物件繼承
- 物件導向之_繼承概念物件繼承
- Javascript物件導向與繼承JavaScript物件繼承
- 為什麼更推薦使用組合而非繼承關係?繼承
- Android中Module之間介面呼叫發現不了繼承關係Android繼承
- 繼承關係裡的六個預設成員函式繼承函式
- odoo 繼承(owl繼承、web繼承、view繼承)Odoo繼承WebView
- C++標準庫中檔案流類的繼承關係C++繼承
- Python父子關係——繼承(反恐精英案例,應用與練習)Python繼承
- 21. 物件導向之繼承物件繼承
- Javascript實現物件導向繼承JavaScript物件繼承
- JavaScript 複習之 物件的繼承JavaScript物件繼承
- 課時38:類與物件:繼承物件繼承
- <十>關於菱形繼承繼承
- JAVA物件導向高階一:繼承Java物件繼承