Kotlin copy() 函数正在 JPA 实体上创建一个新 ID,从而产生一个新行
Posted
技术标签:
【中文标题】Kotlin copy() 函数正在 JPA 实体上创建一个新 ID,从而产生一个新行【英文标题】:Kotlin copy() function is creating a new Id on a JPA Entity resulting in a new row 【发布时间】:2019-03-25 09:29:48 【问题描述】:我在 spring 控制器上有一个更新方法,它接收请求并使用 copy 将内容复制到加载的实体中。一旦调用了 copy(),实体上的 id 属性就会更改为新属性。
@PutMapping("/id")
fun update(@PathVariable id: UUID, @RequestBody request: UpdateSocietyRequest): ResponseEntity<SocietyUpdatedResponse>
val society = societyRepository.findById(id).orElse(null) ?: return notFound().build()
val updatedSociety = society.copy(
name = request.name,
phone = request.phone,
address = Address(
request.addressLine1,
request.addressLine2,
request.addressLine3,
request.city,
request.state,
request.zipCode
)
)
societyRepository.save(updatedSociety)
return ok(SocietyUpdatedResponse(updatedSociety.id, updatedSociety.name, updatedSociety.phone))
Entity.kt
@MappedSuperclass
@JsonIgnoreProperties(value = ["createdOn, updatedOn"], allowGetters = true)
@EntityListeners(AuditingEntityListener::class)
abstract class BaseEntity
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: UUID = UUID.randomUUID()
@Column(nullable = false, updatable = false)
@CreatedDate
var createdOn: LocalDateTime = LocalDateTime.now()
@Column(nullable = true)
@LastModifiedDate
var updatedOn: LocalDateTime? = null
@Column(nullable = false, updatable = false)
@CreatedBy
var createdBy: String? = null
@Column(nullable = true)
@LastModifiedBy
var updatedBy: String? = null
@Embeddable
data class Address(val addressLine1: String,
val addressLine2: String,
val addressLine3: String,
val city: String,
val state: String,
val zipCode: Int)
@Entity
data class Society(
@NotNull
val name: String,
@NotNull
val phone: String,
@Embedded
val address: Address
) : BaseEntity()
据我所知,副本不应该创建新对象,而是使用请求中的新值更改现有对象。为什么要为 id 分配一个新的 UUID?
谢谢
【问题讨论】:
copy() 顾名思义,创建对象的副本。如果它改变了原始对象,它的 vals 就会被改变,而 vals 不能被改变。 是的,所以 updatedSociety 是从存储库加载的社团对象的副本,并使用副本中的值进行更新......为什么在 updatedSociety 上会有一个新的 UUID? 因为唯一复制的属性是在数据类的主构造函数中声明的属性。id
是一个 val,由超类用 new UUID()
初始化。再一次,一个 val 不可能被重新分配。所以它不可能有除new UUID()
之外的任何其他值。此外,文档说:请注意,编译器仅将主构造函数中定义的属性用于自动生成的函数。。其他说明:为什么要自己分配一个UUID,还要指定这个uuid应该是JPA生成的?
【参考方案1】:
根据this link,只有括号之间定义的属性将在 copy() 函数中使用,然后 id 和其他从超类继承的属性将不会被使用。我测试过了。
类主体中声明的属性
注意编译器只使用 在主构造函数中定义的属性 自动生成的函数。从 生成的实现,在类体内声明它:
数据类 Person(val name: String) var age: Int = 0
只有属性名称会在 toString()、equals()、hashCode() 和 copy() 实现中使用,并且 只有一个组件函数component1()。虽然两个 Person 对象可以有不同的年龄,他们将被平等对待。
但是,我的解决方案是:
不要使用copy()
函数。只需更改society
的属性并保存即可。您可以更改val
的属性,但不能更改引用(禁止society = something
)。
当你使用copy()
时,它会在堆中生成一个新对象,并且它的引用是延迟的(hashCode()
是延迟的)。
所以我认为将 Society 类属性改为 var 并使用以下代码一定很好:
@Entity
data class Society(
@NotNull
var name: String,
@NotNull
var phone: String,
@Embedded
var address: Address
) : BaseEntity()
...
@PutMapping("/id")
fun update(@PathVariable id: UUID, @RequestBody request: UpdateSocietyRequest): ResponseEntity<SocietyUpdatedResponse>
val society = societyRepository.findById(id).orElse(null) ?: return notFound().build()
society.name = request.name
society.phone = request.phone
society.address = Address(
request.addressLine1,
request.addressLine2,
request.addressLine3,
request.city,
request.state,
request.zipCode
)
)
societyRepository.save(society)
return ok(SocietyUpdatedResponse(society.id, society.name, society.phone))
另外,我认为在数据类中使用继承会造成混淆,因此最好避免使用它。
【讨论】:
感谢 Mostafa,如果我将属性更改为 var,上面的代码就可以工作。以上是关于Kotlin copy() 函数正在 JPA 实体上创建一个新 ID,从而产生一个新行的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Kotlin 在 Spring JPA 中正确查询实体
Spring JPA/Hibernate Repository findAll 在 Kotlin 中默认执行 N+1 个请求而不是 JOIN