[英]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.