Функция Kotlin copy () создает новый идентификатор для объекта JPA, в результате чего появляется новая строка

У меня есть метод обновления на контроллере Spring, который принимает запрос и использует копию для копирования содержимого в загруженный объект. Как только вызывается 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 не смогли бы мутировать.

JB Nizet 20.10.2018 23:23

Да, значит, updatedSociety - это копия объекта общества, загруженная из репозитория и обновленная значениями внутри копии .... зачем нужен новый UUID в updatedSociety?

Chirdeep Tomar 20.10.2018 23:43

Потому что копируются только те свойства, которые объявлены в основном конструкторе класса данных. id - это val, инициализированное с помощью new UUID() суперклассом. И val, опять же, невозможно переназначить. Поэтому он не может иметь никакой другой ценности, кроме new UUID(). Кроме того, в документации написано: Обратите внимание, что компилятор использует свойства, определенные внутри основного конструктора, только для автоматически сгенерированных функций.. Другое примечание: почему вы назначаете UUID самостоятельно, а также указываете, что uuid должен быть сгенерирован JPA?

JB Nizet 21.10.2018 08:50
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
3
3
1 131
1

Ответы 1

Согласно эта ссылка в функции copy () будут использоваться только свойства, определенные в круглых скобках, тогда id и другие свойства, унаследованные от суперкласса, не будут использоваться. Я это тестировал.

Properties Declared in the Class Body

Note that the compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body:

data class Person(val name: String) { var age: Int = 0 }

Only the property name will be used inside the toString(), equals(), hashCode(), and copy() implementations, and there will only be one component function component1(). While two Person objects can have different ages, they will be treated as equal.

Однако мое решение:

Не используйте функцию 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))
    }

Кроме того, я думаю, что использование наследования в классах данных может ввести в заблуждение, поэтому лучше его избегать.

Спасибо Мостафе, приведенный выше код будет работать, если я изменю свойства на var.

Chirdeep Tomar 24.10.2018 00:07

Другие вопросы по теме