Kotlin JPA封装了一对多 [英] Kotlin JPA Encapsulate OneToMany

查看:72
本文介绍了Kotlin JPA封装了一对多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Kotlin中使用JPA,遇到了一个试图封装OneToMany关系的问题.这是我在Java中可以轻松实现的功能,但是由于Kotlin仅具有属性而没有类中的字段,因此存在一些问题.

I am using JPA with Kotlin and coming against an issue trying to encapsulate a OneToMany relationship. This is something I can easily achieve in Java but having some issues due to Kotlin only having properties and no fields in classes.

我有一个订单,一个订单有一对多订单项.订单对象具有LineItem的MutableList,但get方法不应返回可变列表或调用者可能修改的任何内容,因为这会破坏封装.订单类应负责管理订单项的收集并确保符合所有业务规则/验证.

I have an order, and an order has one to many line items. The order object has a MutableList of LineItem BUT the get method SHOULD NOT return a mutable list, or anything that the caller could potentially modify, as this breaks encapsulation. The order class should be responsible for managing collection of line items and ensuring all business rules / validations are met.

这是我到目前为止提出的代码.基本上我使用的是后备属性,即Order类将对其进行突变的MutableList,然后有一个返回Iterable的瞬态属性,并且Collections.unmodifiableList(_lineItems)确保即使调用者获得了该列表并将其转换为MutableList,它们也将不会丢失.不能修改它.

This is the code I've come up with thus far. Basically I'm using a backing property which is the MutableList which Order class will mutate, and then there is a transient property which returns Iterable, and Collections.unmodifiableList(_lineItems) ensure that even if caller gets the list, and cast it to MutableList they won't be able to modify it.

有没有更好的方法来实施封装和完整性.也许我只是在我的设计和方法上过于防御.理想情况下,没有人应该使用getter来获取和修改列表,但是嘿,它确实发生了.

Is there a better way to enforce encapsulation and integrity. Perhaps I'm just being too defensive with my design and approach. Ideally no one should be using the getter to get and modify the list, but hey it happens.

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
    }
}

推荐答案

您的基本想法是正确的,但我会提出一些小的修改:

Your basic idea is correct but, I would propose some slight modifications:

@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,因为默认值已经表示未持久".
  • 使用主构造函数,不需要受保护的空主构造函数(请参见
  • the id does not need to be nullable. 0 as default value already means "not persisted".
  • use a primary constructor, there is no need for a protected empty primary constructor (see no-arg compiler plug-in)
  • The id will be auto generated and should not be in the constructor
  • since the id is not part of the primary constructor equals would yield the wrong results (because id would not be part of comparison) unless overriden in a correct manner, so I would not use a data class
  • if lineItems is property without backing field there is not need for a @Transient annotation
  • it would be better to use a block body for addLineItem since it returns Unit, which also enables you to use the += operator instead of an explicit function call (plusAssign).

这篇关于Kotlin JPA封装了一对多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆