简体   繁体   English

一对多/多对一关系未保存在 Kotlin 中,而在 Java 中

[英]One to Many / Many to one relationship isn't saved in Kotlin, while it is in Java

I have a Kotlin project..我有一个 Kotlin 项目..

I added the complete code to github , so you can download it to play around.我将完整的代码添加到 github ,所以你可以下载它来玩。

there are 2 Entities:有2个实体:
Band乐队

@Entity
data class Band(

    @Id
    @Setter(AccessLevel.NONE)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(hidden = true)
    val id: Int? = null,

    var name: @NotEmpty(message = "name must not be empty") String? = null,

    @OneToMany(fetch = FetchType.EAGER,
                mappedBy = "band",
                cascade = [CascadeType.ALL])
    val links: Set<Link> = mutableSetOf()
)

Links链接

@Entity
data class Link(

    @Id
    @Setter(AccessLevel.NONE)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(hidden = true)
    val id: Int? = null,

    var url: String? = null,

    @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL])
    @JoinColumn(name = "band_id", nullable = true)
    var band: Band? = null

)

SQL looks like this: SQL 看起来像这样:

create table band
(
    id serial PRIMARY KEY not null,
    name varchar(100) not null
);

create table link
(
    id serial PRIMARY KEY not null,
    band_id int,
    url varchar(100) not null,

    CONSTRAINT "fk_band_links" FOREIGN KEY ("band_id") REFERENCES "band" ("id")    
);

I create a band like this for my test (in BandUtils.kt )我为我的测试创建了一个这样的乐队(在BandUtils.kt中)

fun createBandWithLinks(): Band {
        val link1 = Link()
        link1.url = "https://fb.com/"

        val link2 = Link()
        link2.url = "https://twitter.com/"

        return Band(
            name = "Band with links",
            links = mutableSetOf(link1,link2),
        )
    }

When I save the data:当我保存数据时:

bandRepository!!.save(BandUtils.createBandWithLinks())

I expect, that the links and the bands are saved.我希望,链接和乐队被保存。 This works.这行得通。

But I can't see the bands in the link table.但我看不到链接表中的波段。

I made the same thing before in Java, and I also see the examples from Baeldung work like this.我之前在 Java 中做过同样的事情,我也看到Baeldung的例子是这样工作的。

Is there any difference between Kotlin and Java? Kotlin 和 Java 有什么区别吗?

Problem 1. Persisting bidirectional relationship Band=Link问题1.保持双向关系Band=Link
In the case of a bidirectional relationship Hibernate (or JPA implementations) cares only about the owning side of the association.在双向关系的情况下,Hibernate(或 JPA 实现)只关心关联的拥有方。 The owning side is the side that doesn't have the mappedBy attribute!拥有方是没有mappedBy属性的一方!
So if we only call band.links = LinkUtils.createListOfLinks() , the Band will not be linked to the new Link entity, because this is not the owning /tracked side of the relation.因此,如果我们只调用band.links = LinkUtils.createListOfLinks() ,则Band将不会链接到新的Link实体,因为这不是关系的拥有/跟踪方。

You need to explicitly set Band for Link , call link.band = band , because that is the owning side of the relation.您需要为Link显式设置Band ,调用link.band = band ,因为这是关系的拥有方。

When using mappedBy , it is the responsibility of the developer to know what is the owning side, and update the correct side of the relation in order to trigger the persistence of the new relation in the database.使用mappedBy时,开发人员有责任知道拥有方是什么,并更新关系的正确方以触发数据库中新关系的持久性。

There are no differences between Jawa and Kotlin. Jawa 和 Kotlin 之间没有区别。 It is JPA specification.它是 JPA 规范。

Solution 1: set relation from both sides解决方案1:从双方设置关系
Correct Utils to set relation from both sides更正 Utils 以设置双方的关系

object BandUtils {
    fun createBandWithLinks(): Band {
         val band = Band(
            name = "MEA with links",
            description = "merch em all"
            )
        band.links = LinkUtils.createListOfLinks(band)
        return band
    }
}

object LinkUtils {
    fun createListOfLinks(band: Band): MutableSet<Link> {
        val link1 = Link()
        link1.url = "https://fb.com/joergi"
        link1.band = band

        val link2 = Link()
        link2.url = "https://twitter.com/joergi"
        link2.band = band

        var linkSets = mutableSetOf<Link>()
        linkSets.add(link1)
        linkSets.add(link2)
        return linkSets
    }
}

Solution 2: change the owning side of the relation (NOT recommended)解决方案 2:更改关系的拥有方(不推荐)
No need to change your utils.无需更改您的实用程序。
Remove mappedBy for OneToMany , add JoinColumn删除OneToManymappedBy ,添加JoinColumn

    @OneToMany(
        fetch = FetchType.EAGER,
       // mappedBy = "band",
        cascade = [CascadeType.ALL]
    )
    @JoinColumn(name = "band_id")
    var links: MutableSet<Link> = mutableSetOf()

Add JoinColumn(insertable = false, updatable = false) for ManyToOne为 ManyToOne 添加JoinColumn(insertable = false, updatable = false) ManyToOne = false)

    @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL])
    @JoinColumn(name = "band_id", insertable = false, updatable = false)
    var band: Band? = null

Entities definition实体定义

@Entity
data class Band(

    @Id
    @Setter(AccessLevel.NONE)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(hidden = true)
    val id: Int? = null,

    var name: @NotEmpty(message = "name must not be empty") String? = null,

    var description: String? = null,

    @JsonBackReference(value = "links")
    @OneToMany(
        fetch = FetchType.EAGER,
        cascade = [CascadeType.ALL]
    )
    @JoinColumn(name = "band_id")
    var links: MutableSet<Link> = mutableSetOf()
}

@Entity
data class Link(

    @Id
    @Setter(AccessLevel.NONE)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(hidden = true)
    val id: Int? = null,


    var url: String? = null

) {
    @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL])
    @JoinColumn(name = "band_id", insertable = false, updatable = false)
    var band: Band? = null
}

In your Baeldung article this problem is described in item 6.在您的Baeldung文章中,此问题在第 6 项中进行了描述。

Problem 2. Bidirectional relationship data class circular dependency for toString and hashCode问题2. toString和hashCode的双向关系数据class循环依赖
When you perform the correct creation of your entities you will receive StackOverflow error during persisting because you have bidirectional relations and entities defined like data class .当您正确创建实体时,您将在持久化期间收到StackOverflow错误,因为您具有双向关系和定义的实体,如data class Kotling will generate incorrect hashCode implementation which moves into an infinity loop during execution for bidirectional entities. Kotling 将生成不正确的hashCode实现,该实现在双向实体执行期间进入无限循环。

Solution 1: Lombok解决方案1:龙目岛
Use Lombok instead of data class and ignore one relation side使用Lombok代替data class并忽略一个关系侧

@Entity
@Data
@EqualsAndHashCode(exclude=["band"])
@ToString(exclude = ["band"])
class Link
@Entity
@Data
class Band

Solution 2: move the properties that cause de circular dependency to the data class body解决方案2:将导致去循环依赖的属性移动到数据class体
See details Kotlin - Data class entity throws StackOverflowError查看详细信息Kotlin - 数据 class 实体抛出 StackOverflowError

@Entity
data class Link(

    @Id
    @Setter(AccessLevel.NONE)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(hidden = true)
    val id: Int? = null,


    var url: String? = null

) {
    @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL])
    @JoinColumn(name = "band_id", nullable = true)
    var band: Band? = null
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM