[英]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
類型)。
在您的映射中,您應該使用單個連接列,因為此連接列將放置在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
作為反向方(帶有免費的強制性@ManyToOne
)產生相同的數據庫模式 - color
表上的外鍵。 這就是為什么@JoinColumn
注釋在兩種情況下看起來都一樣,無論您將它放在哪一邊——目標是在many
產生一個外鍵。
“擁有方”的區別在於應用程序的角度。 JPA/hibernate 僅從擁有方保存關系。
因此,如果您更改擁有方,則必須在應用程序代碼中設置此方(屬性)。 在這種情況下,您必須在Color
中設置Box
,否則 JPA/hibernate 不會創建關系(即使您將Colors
添加到Box
)。 而且,它不會引發任何異常,只是不會創建關系。 下次,您將從數據庫中檢索您的Box
, Color
列表將為空。
您可以在我的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-insertable
和non-updatable
updatable 。 如果這是故意的,那很好(在這種情況下,您的所有 colors 必須預先插入到數據庫中,否則它們將在沒有區域設置的情況下插入)。
請記住,在這種情況下,設置 Locale.GERMAN 對數據庫沒有影響。 它將被默默地忽略,如果您在數據庫中沒有這樣的顏色,它將使用 null 插入。
如果您使用 model 這種關系,您可以將單一顏色(如德語中的黑色)分配給一個Box。 聽起來有點不自然。 通常,我會假設黑色可以分配給許多盒子。 所以,這將是一個ManyToMany
關系。 同樣,如果這是故意的,那很好!
將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.