管理關係

使用JPA時,實體子產生器可以為實體之間建立關係。

介紹

關係僅在使用JPA時有效。如果您選擇使用Cassandra他們將不可用。萬一你用MongoDBCouchbase或者Neo4j關係具有不同的語義,但它們都可以使用。有關Couchbase和MongoDB關係的更多訊息,請參閱Couchbase和MongoDB的嵌入式實體

兩個實體之間存在關係,JHipster將為生成以下程式碼:

  • 使用JPA管理生成實體中的關係
  • 建立正確的Liquibase變更日誌,確認實體的關係存同步到資料庫中
  • 生成Angular/React前端,以便您可以在使用者介面中以圖形方式管理此關係

JHipster UML與JDL Studio

本頁描述如何使用標準指令行介面與JHipster建立關係。如果要建立多個實體和關係,則可能更喜歡使用圖形界面工具。

在這種情況下,有三個選項可用:

  • JDL Studio, 我們的線上工具,可以使用我們特定於域的語言來建立實體和關係。
  • JHipster IDE ,一個外掛,為流行的IDE提供JDL檔案的文字編輯支援。
  • 不推薦:JHipster UML, 它允許您使用UML編輯器。

您可以使用jdl子產生器透過執行jhipster jdl your-jdl-file.jh從JDL檔案生成具有關係的實體。

支援的關係

當我們使用JPA時,可以使用通常的一對多,多對一,多對多和一對一的關係:

提示: User實體

關於它的訊息位於使用者實體

關於實體和關係生成的小警告:在以下範例中,您會注意到在某些情況下 可能 編譯會失敗,因為未生成目標實體,這很正常(可以忽略此警告)。

有兩種方法可以避免這種情況:

  • 首先生成實體,然後生成關係
  • 使用JDL

雙向一對多關係

讓我們從兩個實體開始,一個Owner和一個Car。一位所有者可以擁有多輛汽車,一輛汽車只能擁有一位所有者。

因此,這是一種一對多關係(一個所有者有很多車),而另一側是多對一的關係(多輛汽車車主是一位):

Owner (1) <-----> (*) Car

我們將先建立Owner。以下是與Owner建立時JHipster提出的問題:

jhipster entity Owner
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? owner

請注意,我們選擇使用預設的關係名稱。

現在我們生成 Car

jhipster entity Car
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Owner
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with Angular, which field from 'Owner' do you want to use? id

使用下面的JDL語句也可以實現相同的目的

entity Owner
entity Car

relationship OneToMany {
  Owner{car} to Car{owner}
}

就是這樣,您現在在這兩個實體之間建立了一對多關係!在生成的Angular/React前端UI上,您將在Car下拉選單中選擇Owner

雙向多對一關係

在反轉JDL檔案中的邊之後,這等效於雙向一對多關係:

entity Owner
entity Car

relationship ManyToOne {
  Car{owner} to Owner{car}
}

單向多對一關係

在前面的範例中,我們有一個雙向關係:從Car實例中可以找到它的所有者,從Owner實例中可以得到它的所有汽車。

多對一的單向關係意味著汽車知道其所有者,但反向不行。

Owner (1) <----- (*) Car

您之所以會建立這種關係,有兩個原因:

  • 從業務角度來看,您以這種方式來使用實體。因此,您不希望擁有一個允許開發人員執行無意義的操作的API。

  • 使用 Owner實體時,您可以獲得很小的效能提升(因為它不必管理汽車實體資料的採集)。

在這種情況下,您仍將首先建立Owner,但這一次沒有指定關係:

jhipster entity Owner
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? No

然後 Car實體,與上一個範例一樣:

jhipster entity Car
...
Generating relationships with to entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Owner
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with Angular, which field from 'Owner' do you want to use? id

這將與上一個範例相同,但是您將無法從Owner實體中新增或刪除汽車。在生成的Angular/React前端UI上,您將在Car下拉選單中選擇Owner

這是相應的JDL:

entity Owner
entity Car

relationship ManyToOne {
  Car{owner} to Owner
}

單向一對多關係

一對多的單向關係意味著Owner實例可以獲取其汽車集合,但反向不行。與前面的範例相反。

Owner (1) -----> (*) Car

目前,JHipster中預設不提供這種型別的關係,有關更多訊息,請參見#1569

You have two solutions for this: 您有兩種解決方案:

  • 進行雙向對映,並且無需修改即可使用:這是我們推薦的方法,因為它更簡單
  • 進行雙向對映,然後對其進行修改以將其轉換為單向對映:

JDL不支援此功能,因為JHipster中不支援。

同一對實體上的兩個一對多關係

對於此範例,一個Person可以是許多汽車的所有者,還可以是許多汽車的駕駛員:

Person (1) <---owns-----> (*) Car
Person (1) <---drives---> (*) Car

為此,我們需要使用關係名稱,我們在前面的範例中保留了它們的預設值。

生成Person實體,該實體與Car實體具有兩個一對多的關係:

jhipster entity Person
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? ownedCar
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? owner
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? drivedCar
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? driver

生成Car實體,該實體使用與Person實體中設定中相同的關係名稱:

jhipster entity Car
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Person
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with Angular, which field from 'Person' do you want to use? id
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Person
? What is the name of the relationship? driver
? What is the type of the relationship? many-to-one
? When you display this relationship with Angular, which field from 'Person' do you want to use? id

使用下面的JDL也可以實現相同的目的

entity Person
entity Car

relationship OneToMany {
  Person{ownedCar} to Car{owner}
}

relationship OneToMany {
  Person{drivedCar} to Car{driver}
}

現在,Car可以具有駕駛員和所有者,這兩者都是Person實體。在生成的Angular/React前端UI上,您將在Car下拉選單中選擇owner欄位和driver欄位的Person

多對多關係

Driver可以駕駛許多汽車,但是 Car也可以具有許多駕駛員。

Driver (*) <-----> (*) Car

在資料庫視角,這意味著我們將在Driver表和Car表之間有一個聯接表。

對於JPA,這兩個實體之一需要管理該關係:在我們的範例中,是Car實體,它將負責新增或刪除駕駛員。

請注意,生成實體後,產生器將通知您在生成檔案時發生了一些錯誤。 這是正常的,因為尚未生成目標實體,因此您可以放心地忽略此警告。

讓我們生成具有多對多關係的關係的非所有權方Driver

jhipster entity Driver
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? many-to-many
? Is this entity the owner of the relationship? No
? What is the name of this relationship in the other entity? driver

然後生成Car,具有多對多關係的所有權:

jhipster entity Car
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Driver
? What is the name of the relationship? driver
? What is the type of the relationship? many-to-many
? Is this entity the owner of the relationship? Yes
? What is the name of this relationship in the other entity? car
? When you display this relationship on client-side, This field will be displayed as a String, so it cannot be a Blob id

使用下面的JDL也可以實現相同的目的

entity Driver
entity Car

relationship ManyToMany {
  Car{driver} to Driver{car}
}

就是這樣,您現在在這兩個實體之間建立了多對多關係!在生成的Angular/React前端UI上,您將在Car中有一個多選下拉選單,以選擇多個Driver,因為Car是擁有方。

一對一關係

按照我們的範例,一對一關係意味著Driver只能駕駛一輛Car,而一輛Car只能擁有一名Driver

Driver (1) <-----> (1) Car

讓我們建立關係中的非所有權方,在我們的範例中是Driver

jhipster entity Driver
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? No
? What is the name of this relationship in the other entity? driver

然後生成擁有關係的Car

jhipster entity Car
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Driver
? What is the name of the relationship? driver
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? Yes
? Do you want to use JPA Derived Identifier - @MapsId? No
? What is the name of this relationship in the other entity? car
? When you display this relationship on client-side, which field from 'Driver' do you want to use? This field will be displayed as a String, so it cannot be a Blob id

使用下面的JDL也可以實現相同的目的

entity Driver
entity Car

relationship OneToOne {
  Car{driver} to Driver{car}
}

就是這樣,您現在在這兩個實體之間建立了一對一的關係!在生成的Angular/React前端使用者介面上,您會在Car下拉選單中選擇一個Driver,因Car是擁有方。

有關與JPAFork識別符號一對一使用的更多訊息

單向一對一關係

單向一對一關係意味著citizen實例可以獲取其護照,但passport實例無法獲取其所有者。

Citizen (1) -----> (1) Passport

首先生成Passport實體,與其所有者沒有任何關係:

jhipster entity Passport
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? No

然後, 生成Citizen實體:

jhipster entity Citizen
...
Generating relationships to other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Passport
? What is the name of the relationship? passport
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? Yes
? Do you want to use JPA Derived Identifier - @MapsId? No
? What is the name of this relationship in the other entity? citizen
? When you display this relationship with Angular, which field from 'Passport' do you want to use? id

完成此操作後,Citizen擁有護照,但是在Passport中未定義任何Citizen實例。在生成的Angular/React前端UI上,由於Citizen是擁有者,因此Citizen中將出現一個下拉清單以選擇Passport。 這是相應的JDL:

entity Citizen
entity Passport

relationship OneToOne {
  Citizen{passport} to Passport
}

使用JPAFork識別符號(@MapsId)進行一對一關係

JPAFork識別符號可提供最高效的對映

這是前面的單向一對一範例的相應JDL:

entity Citizen
entity Passport

relationship OneToOne {
  Citizen{passport} to Passport with jpaDerivedIdentifier 
}

這是前面的雙向一對一範例的相應JDL:

entity Driver
entity Car

relationship OneToOne {
  Car{driver} to Driver{car} with jpaDerivedIdentifier 
}

但是,根據業務需求,在某些情況下可能應避免這種情況,因為它具有以下約束: 一旦在擁有方設定了id(主鍵),就無法使用JPA/Hibernate對其關聯值進行修改。無論如何,您都不應對其進行變更。 - 以下是有關用法的一些建議:

在以下情況下使用@MapsId

  • 從屬-擁有方(子實體)緊密依賴於非擁有方(父實體)。

  • 關聯值永遠都不會改變-如果您一旦設定了子實體的ID(主鍵)就永遠不會改變。

    例如,

      class User{}
      class Profile{ @OneToOne @MapsId private User user; } // 個人資料僅適用於該使用者
      class Preferences{ @OneToOne @MapsId private User user; } // 首選項僅適用於該使用者
    

    為使用者建立個人資料或首選項後,它將永遠不會更改為其他使用者。

在以下情況下,請勿使用 @MapsId

  • 不依賴-如果擁有方(子實體)似乎不依賴於非擁有方(父實體)
  • 關聯值是可以更改的-如果您認為子實體將來會引用另一個父實體。

    例如,

      class Car{ @OneToOne @JoinColumn(name="id") Driver currentDriver} // 將來可以由其他駕駛員駕駛汽車
      class Driver{@OneToOne @JoinColumn(name="id") Car drivingCar} // 駕駛員將駕駛其他汽車
    

    汽車和駕駛員的關聯值都可能在將來發生變化。

注意:已知存在一個關於一起使用@OneToOne@MapsId的問題,以及如何避免使用它們

將獲取資料策略設定為全部抓取(FetchType.EAGER)

所有關係都使用預設的JPA抓取策略:

  • 一對多:LAZY
  • 多對一:EAGER
  • 多對多:LAZY
  • 一對一:EAGER

由於FetchType.EAGER,存在一個JSON反序列化期間存在NPE的已知問題 。 如果要將OneToManyManyToMany關係設定為FetchType.EAGER,則可以使用以下解決方案之一:

  • 使用 @JsonInclude(JsonInclude.Include.NON_EMPTY) 在關係上

    如:

      @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
      @JsonInclude(JsonInclude.Include.NON_EMPTY)
      private Set<Child> child = new HashSet<>();
    
  • 如果在後端獲取資源時集合為空,則回傳null
  • 使用DTO處理空集合的邊緣情況

Couchbase和MongoDB的嵌入式實體

Couchbase和MongoDB透過嵌入式文件支援關係。 有關MongoDB中嵌入式文件的更多訊息,請參考https://docs.mongodb.com/manual/applications/data-models-relationships/ 和有關Couchbase的訊息,請參考https://docs.couchbase.com/server/5.1/data-modeling/modeling-relationships.html

您可以簡單地透過使用@embedded來定義嵌入式文件。 例如定義一對一的關係;

entity Country {
  countryName String
}

@embedded
entity Region {
  regionName String
}


relationship OneToOne {
  Country to Region
}

同樣,對於一對多關係,

entity Country {
  countryName String
}

@embedded
entity Region {
  regionName String
}


relationship OneToMany {
  Country to Region
}

對於多對多關係,您可以簡單地雙向使用@ embedded關鍵字。

@embedded
  entity Country {
  countryName String
}

@embedded
entity Region {
  regionName String
}


relationship ManyToMany {
  Country to Region
}