Spring 意外的JPA行为-在应该更新时删除

Spring 意外的JPA行为-在应该更新时删除,spring,hibernate,jpa,Spring,Hibernate,Jpa,因此,我正在使用一些Spring数据JPA代码,实现是Hibernate。我创造了两个实体,它们有着一对一的关系。以下是实体: @Entity @Table(name = "clients") data class Client ( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long, @Column(unique = true

因此,我正在使用一些Spring数据JPA代码,实现是Hibernate。我创造了两个实体,它们有着一对一的关系。以下是实体:

@Entity
@Table(name = "clients")
data class Client (
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Long,
        @Column(unique = true)
        val name: String,
        @Column(unique = true)
        val clientKey: String,
        val clientSecret: String,
        val enabled: Boolean,
        val accessTokenTimeoutSecs: Int,
        val refreshTokenTimeoutSecs: Int,
        val authCodeTimeoutSecs: Int,

        @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "clientId")
        val clientRedirectUris: List<ClientRedirectUri>
)

@Entity
@Table(
        name = "client_redirect_uris",
        uniqueConstraints = [
                UniqueConstraint(columnNames = [
                        "clientId",
                        "redirectUri"
                ])
        ]
)
data class ClientRedirectUri (
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Long,
        val clientId: Long,
        val redirectUri: String
)
我首先创建authClient变量,然后将其持久化到数据库中。现在已经为它分配了一个ID,我将该ID用于ClientRedirectUri实体的外键关系。设置子实体并将其添加到authClient后,我会再次保存它。到目前为止,一切都很完美

然后,作为测试,我使用Kotlin copy()方法创建了原始对象的克隆,但随后我用一个新的URI替换了ClientDirectURI属性和一个新的列表。这里的目标是不可变地修改实体,然后在数据库中更新它

这将导致一个例外情况:

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["FKCW186RT6R4DRLEKPENRLQDNL5: PUBLIC.CLIENT_REDIRECT_URIS FOREIGN KEY(CLIENT_ID) REFERENCES PUBLIC.CLIENTS(ID) (1)"; SQL statement:
delete from clients where id=? [23503-200]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
实际上,它不是执行更新操作,而是尝试先删除客户机引用,然后插入新引用。这不是我想要的,我想要它做一个更新

现在,经过多次实验,我发现了更令人困惑的事情。让我们对代码示例中的最后一行做一个更改,并返回保存的实体:

authClient = clientRepo.save(authClient.copy(clientRedirectUris = listOf(clientRedirectUri.copy(redirectUri = "uri_1"))))
仅仅做一个小小的调整就可以改变行为,JPA/Hibernate最终可以完美地处理事情。原始客户机实体(根据记录的SQL)不会被触及,而是只处理ClientRedirectUri实体的插入/删除操作

为什么会发生这种情况?我怎样才能在将来防止这种情况发生

authClient = clientRepo.save(authClient.copy(clientRedirectUris = listOf(clientRedirectUri.copy(redirectUri = "uri_1"))))