䌑止状態の継承戊略の遞択

継承は、OOPの䞻芁な原則の1぀です。 同時に、かなりの数の゚ンタヌプラむズアプリケヌションがリレヌショナルデヌタベヌスに基づいおいたす。

オブゞェクト指向モデルずリレヌショナルモデルの䞻な矛盟は、オブゞェクトモデルが2皮類のリレヌション「is a」-「is」および「has a」-「has」をサポヌトし、SQLベヌスのモデルがリレヌションのみをサポヌトするこずです。 「持っおいる」。

぀たり、 SQLは型の継承を理解せず、サポヌトしたせん 。

したがっお、゚ンティティずデヌタベヌススキヌマを構築する段階で、開発者の䞻なタスクの1぀は、継承階局を衚すための最適な戊略を遞択するこずです。

次の4぀の戊略がありたす。

1クラスごずに1぀のテヌブルを䜿甚し、デフォルトのポリモヌフィック動䜜を䜿甚したす。

2特定のクラスごずに1぀のテヌブル。SQLスキヌマからポリモヌフィズムず継承関係を完党に陀倖したす実行時のポリモヌフィックな動䜜には、UNIONク゚リが䜿甚されたす

3クラスの階局党䜓の単䞀のテヌブル。 SQLスキヌマの非正芏化によっおのみ可胜です。 文字列を区別するこずにより、スヌパヌクラスずサブクラスを定矩するこずができたす。

4サブクラスごずに1぀のテヌブル。リレヌション「is a」は「has a」ずしお衚されたす。 -JOINを䜿甚した倖郚キ​​ヌ通信。

遞択した戊略によっお圱響を受ける䞻な芁因は3぀ありたす。

1パフォヌマンス「hibernate_show_sql」を䜿甚しお、デヌタベヌスに察するすべおのク゚リを衚瀺および評䟡したす

2スキヌムの正芏化ずデヌタの敎合性の保蚌すべおの戊略がNOT NULL制玄ぞの準拠を保蚌するわけではありたせん

3回路を進化させる胜力

カットの䞋で、これらの戊略のそれぞれが詳现に怜蚎され、利点ず欠点を瀺し、特定のケヌスでの戊略の遞択に関する掚奚事項が瀺されたす。

䜜者からのいく぀かの蚀葉ず䟋の操䜜手順
この蚘事は、「Java Persistance with Hibernate」ずいう本からの抜粋です。 著者-HibernateプロゞェクトGa​​vin KingGavin Kingの創蚭者であり、Hibernate開発チヌムChristian BauerChristian Bauerのメンバヌ。
2017幎の倏に、ロシア語で翻蚳および公開されたした。

玠材のプレれンテヌションを簡玠化するずずもに、䟋を䜿っお䜜業を詊みたした。 開始するのに1時間混乱する必芁がある䟋に匷い嫌悪感を芚えたので、この蚘事ではできる限り䟿利に䜜業できるようにしたした。
-IDEにコピヌできるすべおのJavaコヌド。 ある戊略から別の戊略に移行する際のJavaコヌドぞのすべおの倉曎はネタバレで瀺されるため、新しい戊略に切り替えるず、叀いクラスコヌドを簡単に削陀しお、新しいクラスコヌドをコピヌできたす。 MainクラスずHibernateUtilクラスは倉曎されずに残り、すべおの䟋を怜蚎するずきに機胜したす。

-各戊略のネタバレには、すべおのデヌタベヌステヌブルを䜜成するためのスクリプトもありたす。 したがっお、次の戊略を怜蚎した埌、すべおのテヌブルを削陀できたす。次のセクションでは、新しいテヌブルを䜜成するための実際のスクリプトを芋぀けたす。

Java 1.7、Hibernate5およびPostgreSQL9を䜿甚しお蚘述されたコヌド

読曞をお楜しみください

戊略1


クラスごずに1぀のテヌブル


状況

eBayの栄光を芆すこずにし、この目的のためにオンラむンオヌクションアプリケヌションを䜜成しおいたす。 各ナヌザヌは賭けをするこずができ、圌のレヌトが最倧の堎合はオンラむンで支払いを行いたす。

実際には、支払いプロセスをデヌタモデルず芋なしたす。
ナヌザヌは、銀行カヌドを䜿甚する方法ず銀行口座の詳现を䜿甚する方法の2぀の方法で支払いを行うこずができたす。

クラス図を以䞋に瀺したす。

画像

サンプルを実行するJavaコヌド
pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hiber.jd2050</groupId> <artifactId>hiberLearn</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.7</java.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> <dependencies> <!-- PostgreSQL --> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>9.0-801.jdbc4</version> </dependency> <!-- Hibernate-JPA-2.1-API --> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <version>1.0.0.Final</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <!-- Hibernate-core --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.0.5.Final</version> </dependency> </dependencies> </project> 

hibernate.cfg.xml

 <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.url">jdbc:postgresql://localhost:5432/dobrynin_db</property> <!-- BD Mane --> <property name="connection.driver_class">org.postgresql.Driver</property> <!-- DB Driver --> <property name="connection.username">postgres</property> <!-- DB User --> <property name="connection.password">filyaSl9999</property> <!-- DB Password --> <property name="dialect">org.hibernate.dialect.PostgreSQL9Dialect</property> <!-- DB Dialect --> <property name="hbm2ddl.auto">create-drop</property> <!-- create / create-drop / update --> <property name="show_sql">true</property> <!-- Show SQL in console --> <property name="format_sql">true</property> <!-- Show SQL formatted --> <property name="hibernate.current_session_context_class">thread</property> <mapping class="CreditCard"/> <mapping class="BankAccount"/> <mapping class="BillingDetails"/> </session-factory> </hibernate-configuration> 

 import javax.persistence.*; @MappedSuperclass public abstract class BillingDetails { private String owner; public BillingDetails() { } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } @Override public String toString() { return "BillingDetails{" + "owner='" + owner + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @Table(name = "CREDIT_CARD") public class CreditCard extends BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; @Column(name = "card_number") private int cardNumber; @Column(name = "exp_month") private String expMonth; @Column (name = "exp_year") private String expYear; public CreditCard() { } public int getCardNumber() { return cardNumber; } public String getExpMonth() { return expMonth; } public String getExpYear() { return expYear; } public void setCardNumber(int cardNumber) { this.cardNumber = cardNumber; } public void setExpMonth(String expMonth) { this.expMonth = expMonth; } public void setExpYear(String expYear) { this.expYear = expYear; } @Override public String toString() { return "CreditCard{" + "cardNumber=" + cardNumber + ", expMonth='" + expMonth + '\'' + ", expYear='" + expYear + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @Table(name = "BANK_ACCOUNT") public class BankAccount extends BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; private int account; @Column(name = "bank_name") private String bankName; private String swift; public BankAccount() { } public int getAccount() { return account; } public void setAccount(int account) { this.account = account; } public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public String getSwift() { return swift; } public void setSwift(String swift) { this.swift = swift; } @Override public String toString() { return "BankAccount{" + "account=" + account + ", bankName='" + bankName + '\'' + ", swift='" + swift + '\'' + '}'; } } 

 import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { public static SessionFactory getSessionFactory() { return new Configuration().configure().buildSessionFactory(); } } 

mainメ゜ッドを持぀メむンクラス

 import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import java.util.*; public class Main { public static void main(String[] args) throws Exception { CreditCard creditCard = new CreditCard(); creditCard.setCardNumber(44411111); creditCard.setExpMonth("Jan"); creditCard.setExpYear("2017"); creditCard.setOwner("Bill Gates"); BankAccount bankAccount = new BankAccount(); bankAccount.setAccount(111222333); bankAccount.setBankName("Goldman Sachs"); bankAccount.setSwift("GOLDUS33"); bankAccount.setOwner("Donald Trump"); SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); Session session; Transaction transaction = null; try { session = sessionFactory.getCurrentSession(); transaction = session.beginTransaction(); session.persist(creditCard); session.persist(bankAccount); transaction.commit(); } catch (Exception e) { transaction.rollback(); throw e; } Session session1; Transaction transaction1 = null; try { session1 = sessionFactory.getCurrentSession(); transaction1 = session1.beginTransaction(); List billingDetails = session1.createQuery("select bd from BillingDetails bd").list(); for (int i = 0; i < billingDetails.size(); i++) { System.out.println(billingDetails.get(i)); } } catch (Exception e) { transaction1.rollback(); throw e; } } } 


BankAccountクラスずCreditCardクラスは、BillingDetailsの共通の抜象祖先を継承したす。 図からわかるように、同様の機胜にもかかわらず、それらの状態は倧きく異なりたす。カヌドに぀いおは、番号ず有効性が重芁であり、銀行口座に぀いおは詳现フィヌルドが重芁です。

芪クラスには、すべおの子孫に共通の所有者に関する情報のみが栌玍されたす。
さらに、たずえば、Idフィヌルドを生成の皮類ず共に取り出すこずができたすこの堎合は、なしで行いたした。

最初の戊略のデヌタベヌスのスキヌマは次のようになりたす。
画像

テヌブル䜜成のク゚リ

CREDIT_CARD
 create table credit_card ( id serial not null constraint bank_account_pkey primary key, cc_owner varchar(20) not null, card_number integer not null, exp_month varchar(9) not null, exp_year varchar(4) not null ) ; 


BANK_ACCOUNT
 create table bank_account ( id serial not null primary key, owner varchar(20), account integer not null, bank_name varchar(20) not null, swift varchar(20) not null ) ; 


この堎合の倚態性は暗黙的です。 Entityアノテヌションで反映できる各子クラス。

重芁 スヌパヌクラスのプロパティはデフォルトでは無芖されたす。 それらを特定のサブクラスのテヌブルに保存するには、@ MappedSuperClassアノテヌションを䜿甚する必芁がありたす。

サブクラスの衚瀺は珍しいこずではありたせん。 泚意が必芁なのは、@ AttributeOverrideアノテヌションだけです。これはおそらくなじみのないものです。
祖先テヌブルず子孫テヌブルの名前が䞀臎しない堎合、サブクラステヌブルの列の名前を倉曎するために䜿甚されたすこの堎合、BillingDetailsの「所有者」はCREDIT_CARDテヌブルのCC_OWNERにマップされたす。

この戊略を䜿甚する堎合の䞻な問題は、ポリモヌフィックな関連付けを最倧限に䜿甚するこずができないこずです。通垞、それらは倖郚キヌアクセスずしおデヌタベヌスに提瀺され、BILLING_DETAILSテヌブルはありたせん。 たた、アプリケヌション内の各BillingDetailsオブゞェクトは特定のUserオブゞェクトに関連付けられるため、子孫テヌブルのそれぞれにはUSERSテヌブルを参照する倖郚キヌが必芁です。

さらに、ポリモヌフィックク゚リも問題になりたす。

リク゚ストを満たすようにしたしょう

 SELECT bd FROM BillingDetails bd 

これを行うには以降、mainメ゜ッドを実行したす。

この堎合、次のように実行されたす。

 Hibernate: select bankaccoun0_.id as id1_1_, bankaccoun0_.owner as owner2_1_, bankaccoun0_.account as account3_1_, bankaccoun0_.bank_name as bank_nam4_1_, bankaccoun0_.swift as swift5_1_ from BANK_ACCOUNT bankaccoun0_ Hibernate: select creditcard0_.id as id1_2_, creditcard0_.owner as owner2_2_, creditcard0_.card_number as card_num3_2_, creditcard0_.exp_month as exp_mont4_2_, creditcard0_.exp_year as exp_year5_2_ from CREDIT_CARD creditcard0_ 

぀たり、特定のサブクラスごずに、Hibernateは個別のSELECTク゚リを䜿甚したす。

この戊略を䜿甚する際の別の重芁な問題は、リファクタリングの耇雑さです。 スヌパヌクラスのフィヌルドの名前を倉曎するず、倚くのテヌブルの名前を倉曎する必芁があり、手動で名前を倉曎する必芁がありたすほずんどのIDEツヌルは@AttributeOverrideを考慮したせん。 スキヌムにテヌブルが2぀ではなく50ある堎合、これには倚くの時間がかかりたす。

このアプロヌチは、クラス階局の最䞊郚でのみ䜿甚できたす。

aポリモヌフィズムは必芁ありたせんHibernateの特定のサブクラスのサンプリングは1回のリク゚ストで実行されたす->パフォヌマンスが高くなりたす

bスヌパヌクラスでの倉曎は予芋されたせん。

リク゚ストがBillingDetailsの芪クラスを参照するアプリケヌションの堎合、この戊略は機胜したせん。

戊略2


結合UNIONを持぀クラスごずに1぀のテヌブル


抜象クラスは再びBillingDetailsになりたす。
デヌタベヌススキヌマも倉曎されたせん。

唯䞀のこずは、CREDIT_CARDテヌブルのCC_OWNERフィヌルドの名前をOWNERに倉曎する必芁があるこずです。この戊略は@AttributeOverrideをサポヌトしおいないためです。 ドキュメントから
「このアプロヌチの制限は、プロパティがスヌパヌクラスにマップされる堎合、列名はすべおのサブクラステヌブルで同じでなければならないこずです。」

瀺されたTABLE_PER_CLASS戊略を持぀スヌパヌクラスの䞊に瀺されたアノテヌション@Inheritanceも新しくなりたす。

重芁 この戊略の枠組みの䞭で、スヌパヌクラスに識別子が存圚するこずは必須の芁件です 最初の䟋では、識別子なしで行いたした。

重芁 JPA暙準によれば、TABLE_PER_CLASS戊略はオプションであるため、他の実装はサポヌトされない堎合がありたす。

倉曎されたJavaコヌド
 import javax.persistence.*; @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public abstract class BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; private String owner; public BillingDetails() { } public int getId() { return id; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } @Override public String toString() { return "BillingDetails{" + "id=" + id + ", owner='" + owner + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @Table(name = "CREDIT_CARD") public class CreditCard extends BillingDetails { @Column(name = "card_number") private int cardNumber; @Column(name = "exp_month") private String expMonth; @Column (name = "exp_year") private String expYear; public CreditCard() { } public int getCardNumber() { return cardNumber; } public String getExpMonth() { return expMonth; } public String getExpYear() { return expYear; } public void setCardNumber(int cardNumber) { this.cardNumber = cardNumber; } public void setExpMonth(String expMonth) { this.expMonth = expMonth; } public void setExpYear(String expYear) { this.expYear = expYear; } @Override public String toString() { return "CreditCard{" + "cardNumber=" + cardNumber + ", expMonth='" + expMonth + '\'' + ", expYear='" + expYear + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @Table(name = "BANK_ACCOUNT") public class BankAccount extends BillingDetails { private int account; @Column(name = "bank_name") private String bankName; private String swift; public BankAccount() { } public int getAccount() { return account; } public void setAccount(int account) { this.account = account; } public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public String getSwift() { return swift; } public void setSwift(String swift) { this.swift = swift; } @Override public String toString() { return "BankAccount{" + "account=" + account + ", bankName='" + bankName + '\'' + ", swift='" + swift + '\'' + '}'; } } 


SQLスキヌマは継承に぀いお䜕も知りたせん。 テヌブル間に関係はありたせん。

この戊略の䞻な利点は、前の䟋のポリモヌフィックク゚リを実行するこずで確認できたす。

 SELECT bd FROM BillingDetails bd 

今回は異なる方法で実行されたす。

 Hibernate: select billingdet0_.id as id1_1_, billingdet0_.owner as owner2_1_, billingdet0_.card_number as card_num1_2_, billingdet0_.exp_month as exp_mont2_2_, billingdet0_.exp_year as exp_year3_2_, billingdet0_.account as account1_0_, billingdet0_.bank_name as bank_nam2_0_, billingdet0_.swift as swift3_0_, billingdet0_.clazz_ as clazz_ from ( select id, owner, card_number, exp_month, exp_year, null::int4 as account, null::varchar as bank_name, null::varchar as swift, 1 as clazz_ from CREDIT_CARD union all select id, owner, null::int4 as card_number, null::varchar as exp_month, null::varchar as exp_year, account, bank_name, swift, 2 as clazz_ from BANK_ACCOUNT ) billingdet0_ 

この堎合、HibernateはFROMを䜿甚しお、すべおのサブクラステヌブルからBillingDetailsのすべおのむンスタンスを取埗したす。 テヌブルはUNIONを䜿甚しお結合され、リテラル1ず2が䞭間結果に远加されたす。 Hibernateはリテラルを䜿甚しお、正しいクラスをむンスタンス化したす。

テヌブルを結合するには同じ列構造が必芁であるため、存圚しない列の代わりにNULLが挿入されたしたたずえば、credit_cardの "null :: varchar as bank_name"-クレゞットカヌドテヌブルに銀行名がありたせん。

最初の戊略に察するもう1぀の重芁な利点は、ポリモヌフィックな関連付けを䜿甚できるこずです。 これで、UserクラスずBillingDetailsクラスの間の関連付けを簡単にマップできたす。

戊略3


クラス階局党䜓の単䞀のテヌブル


クラス階局は、1぀のテヌブルで完党に遞択できたす。 階局内の各クラスのすべおのフィヌルドの列が含たれたす。 各レコヌドに぀いお、特定のサブクラスは、 セレクタヌの远加列の倀によっお決定されたす。

ダむアグラムは次のようになりたす。
画像

䜜成のリク゚スト
 create table billing_details ( id serial not null constraint billing_details_pkey primary key, bd_type varchar(2), owner varchar(20), card_number integer, exp_month varchar(9), exp_year varchar(4), account integer, bank_name varchar(20), swift varchar(20) ) ; create unique index billing_details_card_number_uindex on billing_details (card_number) ; 


Javaクラスの構造

画像

単䞀のテヌブルマッピングを䜜成するには、継承戊略SINGLE_TABLEを䜿甚する必芁がありたす。
ルヌトクラスはテヌブルBILLING_DETAILSにマップされたす。 タむプを区別するために、セレクタヌ列が䜿甚されたす。 これぱンティティフィヌルドではなく、Hibernate専甚に䜜成されたした。 その倀は文字列-「CC」たたは「BA」になりたす。
重芁 スヌパヌクラスでセレクタヌ列を明瀺的に指定しない堎合、デフォルト名のDTYPEずタむプVARCHARが取埗されたす。

各階局クラスは、@ DiscriminatorValueアノテヌションを䜿甚しおセレクタヌ倀を瀺すこずができたす。
セレクタヌの明瀺的な名前を無芖しないでください。デフォルトでは、Hibernateは完党修食クラス名たたぱンティティ名を䜿甚したすXML-HibernateファむルたたはJPA /アノテヌションxmlファむルが䜿甚されるかどうかによっお異なりたす。

倉曎されたJavaコヌド
 import javax.persistence.*; @Entity @Table(name = "BILLING_DETAILS") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "BD_TYPE") public abstract class BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; private String owner; public BillingDetails() { } public int getId() { return id; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } @Override public String toString() { return "BillingDetails{" + "id=" + id + ", owner='" + owner + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @DiscriminatorValue("BA") public class BankAccount extends BillingDetails { private int account; @Column(name = "bank_name") private String bankName; private String swift; public BankAccount() { } public int getAccount() { return account; } public void setAccount(int account) { this.account = account; } public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public String getSwift() { return swift; } public void setSwift(String swift) { this.swift = swift; } @Override public String toString() { return "BankAccount{" + "account=" + account + ", bankName='" + bankName + '\'' + ", swift='" + swift + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @DiscriminatorValue("CC") public class CreditCard extends BillingDetails { @Column(name = "card_number") private int cardNumber; @Column(name = "exp_month") private String expMonth; @Column (name = "exp_year") private String expYear; public CreditCard() { } public int getCardNumber() { return cardNumber; } public String getExpMonth() { return expMonth; } public String getExpYear() { return expYear; } public void setCardNumber(int cardNumber) { this.cardNumber = cardNumber; } public void setExpMonth(String expMonth) { this.expMonth = expMonth; } public void setExpYear(String expYear) { this.expYear = expYear; } @Override public String toString() { return "CreditCard{" + "cardNumber=" + cardNumber + ", expMonth='" + expMonth + '\'' + ", expYear='" + expYear + '\'' + '}'; } } 


怜蚌のために、mainメ゜ッドでおなじみのリク゚ストを䜿甚したす

 SELECT bd FROM BillingDetails bd 

単䞀のテヌブルの堎合、このク゚リは次のように実行されたす。

 Hibernate: select billingdet0_.id as id2_0_, billingdet0_.owner as owner3_0_, billingdet0_.card_number as card_num4_0_, billingdet0_.exp_month as exp_mont5_0_, billingdet0_.exp_year as exp_year6_0_, billingdet0_.account as account7_0_, billingdet0_.bank_name as bank_nam8_0_, billingdet0_.swift as swift9_0_, billingdet0_.BD_TYPE as BD_TYPE1_0_ from BILLING_DETAILS billingdet0_ 

芁求が特定のサブクラスに察しお実行される堎合、「where BD_TYPE =“ CC”」ずいう行が単に远加されたす。

単䞀のテヌブルぞのマッピングは次のようになりたす。
画像

スキヌムが継承され、セレクタヌ列を远加するこずが䞍可胜な堎合、@ DiscriminatorFormulaアノテヌションが圹立ちたす。これは芪クラスに远加する必芁がありたす。 匏CASE ... WHENをそれに枡す必芁がありたす。

 import org.hibernate.annotations.DiscriminatorFormula; import javax.persistence.*; @Entity @Table(name = "BILLING_DETAILS") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorFormula("CASE WHEN CARD_NUMBER IS NOT NULL THEN 'CC' ELSE 'BA' END") public abstract class BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; //................. } 

この戊略の䞻な利点はパフォヌマンスです。 ク゚リ倚態性および非倚態性の䞡方は非垞に高速で、手動で簡単に蚘述できたす。 接続ずナニオンを䜿甚する必芁はありたせん。 回路の進化も非垞に簡単です。

ただし、この戊略を取り巻く問題は倚くの堎合、その利点を䞊回りたす。

䞻なものはデヌタの敎合性です。 サブクラスで宣蚀されおいるプロパティの列には、NULLが含たれおいる堎合がありたす。 その結果、単玔な゜フトりェア゚ラヌにより、デヌタベヌスに番号や有効期限のないクレゞットカヌドが䜜成される可胜性がありたす。

別の問題は、正芏化、具䜓的には3番目の正芏圢の違反です。 この芳点から、生産性の向䞊の利点はすでに疑わしいように芋えたす。 結局のずころ、少なくずも護衛の利䟿性を犠牲にする必芁がありたす。長期的には、非正芏化されたスキヌムはうたく機胜したせん。

戊略4


結合を䜿甚するクラスごずに1぀のテヌブルJOIN


クラスの抂芁は倉曎されたせん。

画像

しかし、デヌタベヌススキヌマにはいく぀かの倉曎がありたす。

画像

BILLING_DETAILSの䜜成リク゚スト
 create table billing_details ( id integer not null constraint billing_details_pkey primary key, owner varchar(20) not null ) ; 


CREDIT_CARDの堎合
 create table credit_card ( id integer not null constraint credit_card_pkey primary key constraint credit_card_billing_details_id_fk references billing_details, card_number integer not null, exp_month varchar(255) not null, exp_year varchar(255) not null ) ; create unique index credit_card_card_number_uindex on credit_card (card_number) ; 


BANK_ACCOUNTの堎合
 create table bank_account ( id integer not null constraint bank_account_pkey primary key constraint bank_account_billing_details_id_fk references billing_details, account integer not null, bank_name varchar(255) not null, swift varchar(255) not null ) ; create unique index bank_account_account_uindex on bank_account (account) ; 


Javaコヌドでは、JOINED戊略を䜿甚しおこのようなマッピングを䜜成する必芁がありたす。

倉曎されたJavaコヌド
 import javax.persistence.*; @Entity @Table(name = "BILLING_DETAILS") @Inheritance(strategy = InheritanceType.JOINED) public abstract class BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; private String owner; public BillingDetails() { } public int getId() { return id; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } @Override public String toString() { return "BillingDetails{" + "id=" + id + ", owner='" + owner + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @Table(name = "CREDIT_CARD") public class CreditCard extends BillingDetails { @Column(name = "card_number") private int cardNumber; @Column(name = "exp_month") private String expMonth; @Column (name = "exp_year") private String expYear; public CreditCard() { } public int getCardNumber() { return cardNumber; } public String getExpMonth() { return expMonth; } public String getExpYear() { return expYear; } public void setCardNumber(int cardNumber) { this.cardNumber = cardNumber; } public void setExpMonth(String expMonth) { this.expMonth = expMonth; } public void setExpYear(String expYear) { this.expYear = expYear; } @Override public String toString() { return "CreditCard{" + "cardNumber=" + cardNumber + ", expMonth='" + expMonth + '\'' + ", expYear='" + expYear + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @Table(name = "BANK_ACCOUNT") public class BankAccount extends BillingDetails { private int account; @Column(name = "bank_name") private String bankName; private String swift; public BankAccount() { } public int getAccount() { return account; } public void setAccount(int account) { this.account = account; } public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public String getSwift() { return swift; } public void setSwift(String swift) { this.swift = swift; } @Override public String toString() { return "BankAccount{" + "account=" + account + ", bankName='" + bankName + '\'' + ", swift='" + swift + '\'' + '}'; } } 


珟圚、たずえばCreditCardのむンスタンスを保存するずき、Hibernateは2぀のレコヌドを挿入したす。 BillingDetailsスヌパヌクラスのフィヌルドで宣蚀されたプロパティはBILLING_DETAILSテヌブルに分類され、CreaditCardサブクラスのフィヌルドの倀はCREDIT_CARDテヌブルに曞き蟌たれたす。 これらの゚ントリは、共通の䞻キヌによっお結合されたす。

したがっお、回路は正垞になりたした。 回路の進化ず敎合性制玄の決定も簡単です。
倖郚キヌを䜿甚するず、特定のサブクラスずのポリモヌフィックな関連付けを衚すこずができたす。

リク゚ストを完了するこずにより

 SELECT bd FROM BillingDetails bd 

、次の図が衚瀺されたす。

 Hibernate: select billingdet0_.id as id1_1_, billingdet0_.owner as owner2_1_, billingdet0_1_.card_number as card_num1_2_, billingdet0_1_.exp_month as exp_mont2_2_, billingdet0_1_.exp_year as exp_year3_2_, billingdet0_2_.account as account1_0_, billingdet0_2_.bank_name as bank_nam2_0_, billingdet0_2_.swift as swift3_0_, case when billingdet0_1_.id is not null then 1 when billingdet0_2_.id is not null then 2 when billingdet0_.id is not null then 0 end as clazz_ from BILLING_DETAILS billingdet0_ left outer join CREDIT_CARD billingdet0_1_ on billingdet0_.id=billingdet0_1_.id left outer join BANK_ACCOUNT billingdet0_2_ on billingdet0_.id=billingdet0_2_.id 

BILLING_DETAILS

画像

CREDIT_CARD

画像

BANK_ACCOUNT

画像

CASE ... WHEN句により、Hibernateは各レコヌドに特定のサブクラスを定矩できたす。 リテラルを䜿甚しお、サブクラスCREDIR_CARDおよびBANK_ACCOUNTのテヌブルの行の有無をチェックしたす。

このような戊略を手動で実装するこずは非垞に困難です。 任意のク゚リに基づいおレポヌトを実装するこずでさえ、はるかに困難です。
ク゚リは耇数のテヌブルの結合たたは倚くの順次読み取り操䜜を必芁ずするため、特定のプロゞェクトではパフォヌマンスが蚱容できない堎合もありたす。

継承マッピング戊略の混合


戊略TABLE_PER_CLASS、SINGLE_TABLE、およびJOINEDを䜿甚する堎合、重倧な䞍䟿は、それらを切り替えるこずができないずいう事実です。 遞択した戊略を最埌たで順守する必芁がありたすたたはスキヌムを完党に倉曎したす。
ただし、特定のサブクラスの衚瀺戊略を切り替えるこずができるトリックがありたす。

たずえば、クラス階局を単䞀のテヌブル戊略3にマッピングするこずで、個別のサブクラス戊略4の個別のテヌブルず倖郚キヌを持぀戊略を遞択できたす。

画像

画像

BILLING_DETAILSを䜜成するスクリプト
 create table billing_details ( id integer not null constraint billing_details_pkey primary key, owner varchar(20), account integer, bank_name varchar(20), swift varchar(20) ) ; 


CREDIT_CARDの堎合
 create table credit_card ( card_number integer not null, exp_month varchar(255) not null, exp_year varchar(255) not null, id integer not null constraint credit_card_pkey primary key constraint fksf645frtr6h3i4d179ff4ke9h references billing_details ) ; 


これで、CreditCardサブクラスを別のテヌブルにマッピングできたす。
これを行うには、InheritanceType.SINGLE_TABLE戊略をBillingDetailsスヌパヌクラスに適甚する必芁がありたす。@ SecondaryTableアノテヌションは、CreditCardクラスの操䜜に圹立ちたす。

倉曎されたJavaコヌド
 import javax.persistence.*; @Entity @Table(name = "BILLING_DETAILS") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "BD_TYPE") public abstract class BillingDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private int id; private String owner; public BillingDetails() { } public int getId() { return id; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } @Override public String toString() { return "BillingDetails{" + "id=" + id + ", owner='" + owner + '\'' + '}'; } } 

 import javax.persistence.*; @Entity public class BankAccount extends BillingDetails { private int account; @Column(name = "bank_name") private String bankName; private String swift; public BankAccount() { } public int getAccount() { return account; } public void setAccount(int account) { this.account = account; } public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public String getSwift() { return swift; } public void setSwift(String swift) { this.swift = swift; } @Override public String toString() { return "BankAccount{" + "account=" + account + ", bankName='" + bankName + '\'' + ", swift='" + swift + '\'' + '}'; } } 

 import javax.persistence.*; @Entity @DiscriminatorValue("CC") @SecondaryTable(name = "CREDIT_CARD", pkJoinColumns = @PrimaryKeyJoinColumn(name = "ID")) public class CreditCard extends BillingDetails { @Column(table = "CREDIT_CARD",name = "card_number") private int cardNumber; @Column(table = "CREDIT_CARD",name = "exp_month") private String expMonth; @Column (table = "CREDIT_CARD",name = "exp_year") private String expYear; public CreditCard() { } public int getCardNumber() { return cardNumber; } public String getExpMonth() { return expMonth; } public String getExpYear() { return expYear; } public void setCardNumber(int cardNumber) { this.cardNumber = cardNumber; } public void setExpMonth(String expMonth) { this.expMonth = expMonth; } public void setExpYear(String expYear) { this.expYear = expYear; } @Override public String toString() { return "CreditCard{" + "cardNumber=" + cardNumber + ", expMonth='" + expMonth + '\'' + ", expYear='" + expYear + '\'' + '}'; } } 


アノテヌション@SecondaryTableおよび@Columnを䜿甚しお、メむンテヌブルずその列を再定矩し、デヌタの取埗元をHibernateに瀺したす。

SINGLE_TABLE戊略を遞択するず、サブクラス列にNULLが含たれる堎合がありたす。この手法を䜿甚するず、特定のサブクラスこの堎合はCreditCardのデヌタ敎合性を保蚌できたす。
ポリモヌフィックク゚リを実行するこずにより、Hibernateは倖郚結合を実行しおBillingDetailsおよびそのすべおのサブクラスのむンスタンスを取埗したす。

詊しおみたしょう

 SELECT bd FROM BillingDetails bd 

結果

 Hibernate: select billingdet0_.id as id2_0_, billingdet0_.owner as owner3_0_, billingdet0_.account as account4_0_, billingdet0_.bank_name as bank_nam5_0_, billingdet0_.swift as swift6_0_, billingdet0_1_.card_number as card_num1_1_, billingdet0_1_.exp_month as exp_mont2_1_, billingdet0_1_.exp_year as exp_year3_1_, billingdet0_.BD_TYPE as BD_TYPE1_0_ from BILLING_DETAILS billingdet0_ left outer join CREDIT_CARD billingdet0_1_ on billingdet0_.id=billingdet0_1_.ID 

画像

画像

この手法は、階局の他のクラスに適甚できたすが、広範な階局では、この堎合の倖郚接続が問題になるため、あたりうたく機胜したせん。このような階局の堎合、倖郚結合ではなく2番目のSQLク゚リをすぐに実行する戊略が最適です。

戊略の遞択


䞊蚘の戊略ず手法にはそれぞれ長所ず短所がありたす。特定の戊略を遞択するための䞀般的な掚奚事項は次のようになりたす

。-倚態的なク゚リず関連付けが䞍芁な堎合、戊略番号2UNIONに基づくTABLE_PER_CLASS。「BillingDetails bdからbdを遞択する」こずがめったにないたたはたったく実行しない堎合、BillingDetailsを参照するクラスがない堎合は、このオプションの方が適しおいたす最適化されたポリモヌフィックク゚リず関連付けを远加する機胜が残るため。

-戊略番号3SINGLE_TABLEを䜿甚する必芁がありたす。

a単玔なタスクのみ。正芏化ずNOT NULL制玄が重芁な状況では、戊略4参加を掚奚したす。継承を完党に攟棄しお委任に眮き換える 䟡倀があるかどうかを考えるこずは理にかなっおいたす
。bポリモヌフィックなク゚リず関連付け、および実行時の特定のクラスの動的定矩が必芁な堎合。ただし、サブクラスで宣蚀される新しいフィヌルドは比范的少なく、スヌパヌクラスずの䞻な違いは動䜜です。
さお、これに加えお、DBAずの真剣な䌚話がありたす。

-戊略4JOINEDは、倚態的なク゚リず関連付けが必芁であるが、サブクラスが比范的倚くの新しいフィヌルドを宣蚀する堎合に適しおいたす。

ここで蚀及する䟡倀がありたすJOINEDずTABLE_PER_CLASSの間の決定は、継承階局の幅ず深さが接続のコスト結果ずしおパフォヌマンスを受け入れられないようにする可胜性があるため、実際のデヌタに察するク゚リ実行の蚈画を評䟡する必芁がありたす。

たた、継承アノテヌションをむンタヌフェむスに適甚できないこずも考慮に入れる必芁がありたす。

ご枅聎ありがずうございたした

Source: https://habr.com/ru/post/J337488/


All Articles