JavaPersistenceWithHibernate第二版笔记-第六章-Mapping inheritance-005Table per subclass with joins(@Inheritance(strategy = InheritanceType.JOINED)、@PrimaryKeyJoinColumn、)
一、结构
The fourth option is to represent inheritance relationships as SQL foreign key associations. Every class/subclass that declares persistent properties—including abstract classes and even interfaces—has its own table.
Unlike the table-per-concrete-class strategy we mapped first, the table of a concrete @Entity here contains columns only for each non-inherited property, declared by the subclass itself, along with a primary key that is also a foreign key of the superclass table.
二、代码
1.
1 package org.jpwh.model.inheritance.joined; 2 3 import org.jpwh.model.Constants; 4 5 import javax.persistence.Entity; 6 import javax.persistence.GeneratedValue; 7 import javax.persistence.Id; 8 import javax.persistence.Inheritance; 9 import javax.persistence.InheritanceType; 10 import javax.validation.constraints.NotNull; 11 12 @Entity 13 @Inheritance(strategy = InheritanceType.JOINED) 14 public abstract class BillingDetails { 15 16 @Id 17 @GeneratedValue(generator = Constants.ID_GENERATOR) 18 protected Long id; 19 20 @NotNull 21 protected String owner; 22 23 // ... 24 25 protected BillingDetails() { 26 } 27 28 protected BillingDetails(String owner) { 29 this.owner = owner; 30 } 31 32 public Long getId() { 33 return id; 34 } 35 36 public String getOwner() { 37 return owner; 38 } 39 40 public void setOwner(String owner) { 41 this.owner = owner; 42 } 43 }
2.
1 package org.jpwh.model.inheritance.joined; 2 3 import javax.persistence.Entity; 4 import javax.validation.constraints.NotNull; 5 6 @Entity 7 public class BankAccount extends BillingDetails { 8 9 @NotNull 10 protected String account; 11 12 @NotNull 13 protected String bankname; 14 15 @NotNull 16 protected String swift; 17 18 // ... 19 20 public BankAccount() { 21 super(); 22 } 23 24 public BankAccount(String owner, String account, String bankname, String swift) { 25 super(owner); 26 this.account = account; 27 this.bankname = bankname; 28 this.swift = swift; 29 } 30 31 public String getAccount() { 32 return account; 33 } 34 35 public void setAccount(String account) { 36 this.account = account; 37 } 38 39 public String getBankname() { 40 return bankname; 41 } 42 43 public void setBankname(String bankname) { 44 this.bankname = bankname; 45 } 46 47 public String getSwift() { 48 return swift; 49 } 50 51 public void setSwift(String swift) { 52 this.swift = swift; 53 } 54 }
3.
1 package org.jpwh.model.inheritance.joined; 2 3 import javax.persistence.Entity; 4 import javax.persistence.PrimaryKeyJoinColumn; 5 import javax.validation.constraints.NotNull; 6 7 @Entity 8 @PrimaryKeyJoinColumn(name = "CREDITCARD_ID") 9 public class CreditCard extends BillingDetails { 10 11 @NotNull 12 protected String cardNumber; 13 14 @NotNull 15 protected String expMonth; 16 17 @NotNull 18 protected String expYear; 19 20 // ... 21 22 public CreditCard() { 23 super(); 24 } 25 26 public CreditCard(String owner, String cardNumber, String expMonth, String expYear) { 27 super(owner); 28 this.cardNumber = cardNumber; 29 this.expMonth = expMonth; 30 this.expYear = expYear; 31 } 32 33 public String getCardNumber() { 34 return cardNumber; 35 } 36 37 public void setCardNumber(String cardNumber) { 38 this.cardNumber = cardNumber; 39 } 40 41 public String getExpMonth() { 42 return expMonth; 43 } 44 45 public void setExpMonth(String expMonth) { 46 this.expMonth = expMonth; 47 } 48 49 public String getExpYear() { 50 return expYear; 51 } 52 53 public void setExpYear(String expYear) { 54 this.expYear = expYear; 55 } 56 }
4.The primary key columns of the BANKACCOUNT and CREDITCARD tables each also have a foreign key constraint referencing the primary key of the BILLINGDETAILS table.Hibernate relies on an SQL outer join for select bd from BillingDetails bd :
1 select 2 BD.ID, BD.OWNER, 3 CC.EXPMONTH, CC.EXPYEAR, CC.CARDNUMBER, 4 BA.ACCOUNT, BA.BANKNAME, BA.SWIFT, 5 case 6 when CC.CREDITCARD_ID is not null then 1 7 when BA.ID is not null then 2 8 when BD.ID is not null then 0 9 end 10 from BILLINGDETAILS BD 11 left outer join CREDITCARD CC on BD.ID = CC.CREDITCARD_ID 12 left outer join BANKACCOUNT BA on BD.ID = BA.ID
For a narrow subclass query like select cc from CreditCard cc , Hibernate uses an inner join:
1 select 2 CREDITCARD_ID, OWNER, EXPMONTH, EXPYEAR, CARDNUMBER 3 from 4 CREDITCARD 5 inner join BILLINGDETAILS on CREDITCARD_ID=ID
三、优缺点
1.优点
(1)The primary advantage of this strategy is that it normalizes the SQL schema.Schema evolution and integrity-constraint definition are straightforward. A foreign key referencing the table of a particular subclass may represent a polymorphic association to that particular subclass.
2.缺点
more difficult to implement by hand—even ad hoc reporting is more complex.
结论:even though this mapping strategy is deceptively simple, our experience is that performance can be unacceptable for complex class hierarchies. Queries always require a join across many tables, or many sequential reads.