繁体   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