簡體   English   中英

JPA:@OneToMany 映射和復合主鍵 - 找不到邏輯列錯誤

[英]JPA: @OneToMany mapping and composite primary key - Logical column not found error

我在將 OneToMany 外鍵映射到復合主鍵時遇到問題。 我已經嘗試了很多解決方案,包括這篇文章@OneToMany 和復合主鍵? .

所以情況是:

我有兩個實體,例如 Box 和 Color,復合主鍵位於子端(Color)。

@Entity
@Table(name = "box")
data class Box(
        @Id
        var id: Int = 0,
        ...
       @OneToMany(cascade = [CascadeType.ALL])
        @JoinColumns(
                JoinColumn(name = "box_id", referencedColumnName = "id"),
                JoinColumn(name = "locale", referencedColumnName = "locale"))
        val colors: List<Color> = emptyList(),
)

@Entity
@Table(name = "color")
data class Color(
        
        @EmbeddedId
        var colorId: ColorId? = null,
) : Serializable

@Embeddable
data class ColorId(
        var id: Int = 0,
        @Column(name = "locale", insertable = false, updatable = false)
        var locale: Locale = Locale.Germany
) : Serializable

因此,在 Box 實體中,我試圖在 Box 和 Color 實體之間創建 OneToMany 映射。 為此,我應該使用 Color 實體的復合主鍵嗎? 如果我嘗試將列連接到復合主鍵(就像我在 Box 實體中所做的那樣),我會收到一條錯誤消息 - 無法找到邏輯列“區域設置”。

我該如何解決這個問題?

或者這個問題的巧妙解決方案是什么?

OneToMany映射將一個Box與多個Colors連接起來。 因此,每個Color都需要指向一個Box ,因此每個Color都有一個外鍵引用Box的主鍵Int類型)。

在此處輸入圖像描述

@OneToMany 作為擁有方

在您的映射中,您應該使用單個連接列,因為此連接列將放置在color表中:

@Entity
@Table(name = "box")
data class Box(
        @Id
        var id: Int = 0,
        ...

        @OneToMany(cascade = [CascadeType.ALL])
        @JoinColumn(name = "box_id", referencedColumnName = "id")
        var colors: MutableList<Color> = mutableListOf()
)

您可以在我的github repo中查看工作示例(Kotlin 和 Java)。

@OneToMany 作為逆(映射)方

數據庫透視

@OneToMany作為擁有方@OneToMany作為反向方(帶有免費的強制性@ManyToOne )產生相同的數據庫模式 - color表上的外鍵。 這就是為什么@JoinColumn注釋在兩種情況下看起來都一樣,無論您將它放在哪一邊——目標是在many產生一個外鍵。

應用視角

“擁有方”的區別在於應用程序的角度。 JPA/hibernate 僅從擁有方保存關系。

因此,如果您更改擁有方,則必須在應用程序代碼中設置此方(屬性)。 在這種情況下,您必須在Color中設置Box ,否則 JPA/hibernate 不會創建關系(即使您將Colors添加到Box )。 而且,它不會引發任何異常,只是不會創建關系。 下次,您將從數據庫中檢索您的BoxColor列表將為空。

您可以在我的github repo中查看工作示例並查看所有權差異。

@Entity
@Table(name = "box")
data class Box(
        @Id
        var id: Int = 0,
        ...

        @OneToMany(cascade = [CascadeType.ALL], mappedBy="box")
        var colors: MutableList<Color> = mutableListOf(),
)

@Entity
@Table(name = "color")
data class Color(
        
        @EmbeddedId
        var colorId: ColorId? = null,
        
        // the owning side is changed, therefore you MUST set the box in Color
        // otherwise the relationship in a database will not be saved (!)
        @ManyToOne
        @JoinColumn(name = "box_id")
        var box: Box? = null
) : Serializable

其他可能的問題

由於您遇到了OneToMany關系,因此考慮此 model 中的一些其他可能問題可能對您有用。

語言環境不可插入和不可更新

您可能會遇到的另一個問題是將locale保存在ColorId中,因為您將其標記為non-insertablenon-updatable updatable 。 如果這是故意的,那很好(在這種情況下,您的所有 colors 必須預先插入到數據庫中,否則它們將在沒有區域設置的情況下插入)。

請記住,在這種情況下,設置 Locale.GERMAN 對數據庫沒有影響。 它將被默默地忽略,如果您在數據庫中沒有這樣的顏色,它將使用 null 插入。

僅分配給一個盒子的顏色

如果您使用 model 這種關系,您可以將單一顏色(如德語中的黑色)分配給一個Box。 聽起來有點不自然。 通常,我會假設黑色可以分配給許多盒子。 所以,這將是一個ManyToMany關系。 同樣,如果這是故意的,那很好!

ColorId 作為主鍵

Locale作為Color主鍵的一部分也有點不自然——德語中的黑色和英語中的黑色作為不同的 colors? colors 本身與語言環境無關。 顏色的名稱取決於區域設置,但更多的是 UI 問題。 同樣,如果這是故意的,那很好,畢竟,這是您的商業模式!

在為您提供解決方案之前,我想為您提供有關@OneToMany@JoinColumns注釋的更多信息。

@OneToMany映射表示應用了該映射的實體具有對另一個實體的許多引用。

ie A Box在您的情況下有很多對Color(s)的引用。

現在到 model 這個,我建議你反轉映射並在 Color 實體上使用@ManyToOne

@Entity
@Table(name = "color")
data class Color(
        
        @EmbeddedId
        var colorId: ColorId? = null,
        @ManyToOne(fetch = LAZY)
        @JoinColumn(name = "box_id"), //referencedColumnName is not needed here as it is inferred as "id"
        var box: Box? = null
) : Serializable

因為外鍵應該是顏色。 您不能有從 Box 到 Color 的外鍵,因為可能有多個 Color。

另外,請注意它不是@JoinColumns ,而是單數@JoinColumn 如果Box實體包含Composite Primary Key ,則需要復數版本。

話雖如此,您可以一起忽略@OneToMany映射,因為如果您需要獲取 Box 的所有 Colors,那么在我看來,您可以而且應該使用Query API。

在這種情況下, @OneToMany映射只是一種方便。 但是,如果您堅持使用@OneToMany映射,那么您可以使用以下內容。

@Entity
@Table(name = "box")
data class Box(
        @Id
        var id: Int = 0,
        ...
       @OneToMany(cascade = [CascadeType.ALL], mappedBy="box") // mappedBy will use the "box" reference from the Color Class
        val colors: List<Color> = emptyList(),
)

你有兩個選擇。 定義連接表@JoinTable(name = "color_box_assignment", joinColumns =...)或使用逆映射:

@Entity
@Table(name = "color")
data class Color(
        
        @EmbeddedId
        var colorId: ColorId? = null,
        @ManyToOne(fetch = LAZY)
        @JoinColumns(
            JoinColumn(name = "box_id", referencedColumnName = "id"),
            JoinColumn(name = "locale", referencedColumnName = "locale"))
        var box: Box? = null
) : Serializable

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM