我在Kotlin中使用JPA,遇到一个试图封装OneToMany关系的问题。这是我在Java中可以轻松实现的功能,但是由于Kotlin仅具有属性且类中没有字段,因此存在一些问题。
我有一个订单,一个订单有一对多订单项。订单对象具有LineItem的MutableList,但get方法不应返回可变列表或调用者可能修改的任何内容,因为这会破坏封装。订单类应负责管理订单项的收集并确保符合所有业务规则/验证。
这是我到目前为止提出的代码。基本上,我使用的是后备属性,即Order类将对其进行突变的MutableList,然后有一个返回Iterable的瞬时属性,并且Collections.unmodifiableList(_lineItems)
确保即使调用者获得了该列表并将其转换为MutableList,它们也不会能够修改它。
有没有更好的方法来实现封装和完整性。也许我只是在我的设计和方法上过于防御。理想情况下,没有人应该使用getter来获取和修改列表,但是嘿,这确实发生了。
import java.util.*
import javax.persistence.*
@Entity
@Table(name = "order")
open class Order {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long? = null
@Column(name = "first_name")
lateinit var firstName: String
@Column(name = "last_name")
lateinit var lastName: String
@OneToMany(cascade = arrayOf(CascadeType.ALL), fetch = FetchType.LAZY, mappedBy = "order")
private val _lineItems: MutableList<LineItem> = ArrayList()
val lineItems: Iterable<LineItem>
@Transient get() = Collections.unmodifiableList(_lineItems)
protected constructor()
constructor(firstName: String, lastName: String) {
this.firstName = firstName
this.lastName = lastName
}
fun addLineItem(newItem: LineItem) {
// do some validation and ensure all business rules are met here
this._lineItems.add(newItem)
}
}
@Entity
@Table(name = "line_item")
open class LineItem {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long? = null
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id", referencedColumnName = "id")
lateinit var order: Order
private set
// whatever properties might be here
protected constructor()
constructor(order: Order) {
this.order = order
}
}
最佳答案
您的基本想法是正确的,但我会提出一些小的修改:
@Entity
class OrderEntity(
var firstName: String,
var lastName: String
) {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "order")
private val _lineItems = mutableListOf<LineItem>()
val lineItems get() = _lineItems.toList()
fun addLineItem(newItem: LineItem) {
_lineItems += newItem // ".this" can be omitted too
}
}
@Entity
class LineItem(
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id")
val order: OrderEntity? = null
){
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0
}
笔记:
id
不必为可为空。 0作为默认值已经表示“未持久”。 id
将自动生成,并且不应在构造函数id
不属于主要构造函数equals
会产生错误的结果(因为id不会成为比较的一部分),除非以正确的方式覆盖,所以我不会使用data class
lineItems
是没有后备字段的属性,则不需要@Transient
批注addLineItem
,因为它返回Unit
,这也使您可以使用+=
运算符而不是显式函数调用(plusAssign
)。