[英]JPA: @OneToMany mapping and composite primary key - Logical column not found error
I have issues with mapping OneToMany foreign key to a composite primary key.我在将 OneToMany 外键映射到复合主键时遇到问题。 I have already tried many solutions, including this post @OneToMany and composite primary keys?我已经尝试了很多解决方案,包括这篇文章@OneToMany 和复合主键? . .
So the situation is:所以情况是:
I have two entities, say, Box & Color and the composite primary key is on the child side (Color).我有两个实体,例如 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
So, in the Box entity, I am trying to create an OneToMany mapping between Box & Color entities.因此,在 Box 实体中,我试图在 Box 和 Color 实体之间创建 OneToMany 映射。 For this should I use the composite primary key of the Color entity?为此,我应该使用 Color 实体的复合主键吗? If I try to join columns to the composite primary key(as I have done in the Box entity) I do get an error saying - unable to locate the logical column "locale".如果我尝试将列连接到复合主键(就像我在 Box 实体中所做的那样),我会收到一条错误消息 - 无法找到逻辑列“区域设置”。
How can I resolve this?我该如何解决这个问题?
Or what is the neat solution for this problem?或者这个问题的巧妙解决方案是什么?
OneToMany
mapping will connect one Box
with many Colors
. OneToMany
映射将一个Box
与多个Colors
连接起来。 Therefore, each Color
needs to point to a Box
, so each Color
has a foreign key referencing the primary key of Box
(of Int
type).因此,每个Color
都需要指向一个Box
,因此每个Color
都有一个外键引用Box
的主键( Int
类型)。
In your mapping, you should use a single join column since this join column will be placed in the color
table:在您的映射中,您应该使用单个连接列,因为此连接列将放置在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()
)
You can check-out the working sample (Kotlin and Java) in my github repo .您可以在我的github repo中查看工作示例(Kotlin 和 Java)。
@OneToMany
as an owning side and @OneToMany
as an inverse side (with a complimentary mandatory @ManyToOne
) produce the same database schema - a foreign key on the color
table. @OneToMany
作为拥有方, @OneToMany
作为反向方(带有免费的强制性@ManyToOne
)产生相同的数据库模式 - color
表上的外键。 That's why @JoinColumn
annotation looks the same in both cases, no matter which side you place it on - the goal is to produce a foreign key on the many
side.这就是为什么@JoinColumn
注释在两种情况下看起来都一样,无论您将它放在哪一边——目标是在many
产生一个外键。
The difference in 'owning-side' is in an application perspective. “拥有方”的区别在于应用程序的角度。 JPA/hibernate saves the relationships only from the owning side. JPA/hibernate 仅从拥有方保存关系。
So, if you change the owning side, you must set up this side (attribute) in your application code.因此,如果您更改拥有方,则必须在应用程序代码中设置此方(属性)。 In this case, you must set up Box
in Color
, otherwise, JPA/hibernate will not create a relationship (even though you added your Colors
to Box
).在这种情况下,您必须在Color
中设置Box
,否则 JPA/hibernate 不会创建关系(即使您将Colors
添加到Box
)。 Moreover, it will not raise any exception, just the relationship will not be created.而且,它不会引发任何异常,只是不会创建关系。 Next time, you will retrieve your Box
from a database, the Color
list will be empty.下次,您将从数据库中检索您的Box
, Color
列表将为空。
You can check-out the working samples and see the difference in ownership in my github repo .您可以在我的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
Since you struggle with a OneToMany
relationship, it might be useful for you to consider some other possible problems in this model.由于您遇到了OneToMany
关系,因此考虑此 model 中的一些其他可能问题可能对您有用。
Another problem, you may encounter, is saving locale
in ColorId
as you marked it as non-insertable
and non-updatable
.您可能会遇到的另一个问题是将locale
保存在ColorId
中,因为您将其标记为non-insertable
和non-updatable
updatable 。 If this is on purpose that's fine (in that case all your colors must be pre-inserted into the database, or they will be inserted without Locale).如果这是故意的,那很好(在这种情况下,您的所有 colors 必须预先插入到数据库中,否则它们将在没有区域设置的情况下插入)。
Please, keep in mind, that setting Locale.GERMAN has no effect on a database in this case.请记住,在这种情况下,设置 Locale.GERMAN 对数据库没有影响。 It will be silently ignored and if you don't have such a color in a database, it will be inserted with null.它将被默默地忽略,如果您在数据库中没有这样的颜色,它将使用 null 插入。
If you model this relationship, you make a single Color (like Black in German) be assigned to only one Box.如果您使用 model 这种关系,您可以将单一颜色(如德语中的黑色)分配给一个Box。 It sounds a bit unnatural.听起来有点不自然。 Usually, I would assume Black can be assigned to many Boxes.通常,我会假设黑色可以分配给许多盒子。 So, it would be a ManyToMany
relationship.所以,这将是一个ManyToMany
关系。 Again, if this is on purpose, that's fine!同样,如果这是故意的,那很好!
It's also a bit unnatural to have Locale
as a part of a primary key in Color
-- Black in German and Black in English as different colors?将Locale
作为Color
主键的一部分也有点不自然——德语中的黑色和英语中的黑色作为不同的 colors? The colors themselves are locale-independent. colors 本身与语言环境无关。 The name of a color is locale-dependent, but it's more a matter of UI.颜色的名称取决于区域设置,但更多的是 UI 问题。 Again, if this is on purpose, that's fine, After all, it's your business model!同样,如果这是故意的,那很好,毕竟,这是您的商业模式!
Before providing you a solution, i would like to give you more info on the @OneToMany
and the @JoinColumns
annotation.在为您提供解决方案之前,我想为您提供有关@OneToMany
和@JoinColumns
注释的更多信息。
A @OneToMany
mapping symbolizes the fact that the Entity which has that mapping applied has many references to the other entity. @OneToMany
映射表示应用了该映射的实体具有对另一个实体的许多引用。
ie A Box
has many references to Color(s)
in your case. ie A Box
在您的情况下有很多对Color(s)
的引用。
Now to model this, I would suggest you reverse the mapping and use @ManyToOne
on the Color entity.现在到 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
Because the foreign key is supposed to be in Color.因为外键应该是颜色。 You cannot have foreign keys from Box to Color as there may be more than one Color.您不能有从 Box 到 Color 的外键,因为可能有多个 Color。
Also, note that it is not @JoinColumns
, it is the singular @JoinColumn
.另外,请注意它不是@JoinColumns
,而是单数@JoinColumn
。 You need the plural version if the Box
entity contains a Composite Primary Key
.如果Box
实体包含Composite Primary Key
,则需要复数版本。
With that being said, you can all together ignore the @OneToMany
mapping, because if you need to get all Colors of a Box, you can and should in my opinion, simply use the Query
API.话虽如此,您可以一起忽略@OneToMany
映射,因为如果您需要获取 Box 的所有 Colors,那么在我看来,您可以而且应该使用Query
API。
A @OneToMany
mapping in this case would only be a convenience.在这种情况下, @OneToMany
映射只是一种方便。 But if you insist on having the @OneToMany
mapping then you can use the following.但是,如果您坚持使用@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(),
)
You have two options.你有两个选择。 Either define a join table @JoinTable(name = "color_box_assignment", joinColumns =...)
or use an inverse mapping:定义连接表@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.