TLDR;
У тебя класс без data!
Да, неплохой такой класс
Использовать data class для JPA сущности оправдано, если id записи генерится на стороне приложения и избыточно, если id генерится на стороне базы данных, так как придется переопределять методы equals и hashcode.
Подробности
Есть класс, отмеченный аннотацией @Entity и @Table. Нужно ли добавлять data перед class?
@Table @Entity class Entity( @Id val id: SomeIdentityType, @Column val name: String )
Ответ на вопрос зависит от того, где генерится id, но вначале разберемся с data class. Data class были созданы для тех, кому лень переопределять методы. Ниже написан data class и представлен bytecode.
data class D(val name: String)
Из bytecode я удалил "лишнее" и оставил только необходимое для дальнейших рассуждений.
public static final class D { @NotNull private final String name; @NotNull public final String getName() { return this.name; } @NotNull public String toString() { return "D(name=" + this.name + ")"; } public int hashCode() { String var10000 = this.name; return var10000 != null ? var10000.hashCode() : 0; } public boolean equals(@Nullable Object var1) { if (this != var1) { if (var1 instanceof Scratch_7.D) { Scratch_7.D var2 = (Scratch_7.D)var1; if (Intrinsics.areEqual(this.name, var2.name)) { return true; } } return false; } else { return true; } } }
Таким образом, data перед class это указание компилятору переопределить методы toString(), equals(), hashCode() на основании данных полей конструктора.
JPA Entity это объекты, которые отображают поля объекта на строки в таблице базе данных. На первый взгляд data класс это то, что нужно, но тут есть несколько моментов (не нюансов). По сути все крутится вокруг поля, которое отображается на "первичный ключ" (id). Id может быть сгенерирован как со стороны приложения, так и со стороны базы данных. В первом случае нужен тип, который однозначно идентифицирует запись и примером такого типа может быть UUID.
@Table @Entity data class EntityApp( @Id val id: UUID, @Column val name: String ) @Test fun `test app generated id`() { val name = "имя" val id = UUID.randomUUID() val (entity1, entity2) = EntityApp(id, name) to EntityApp(id, name) val m = mutableMapOf<EntityApp, String>() m[entity1] = "Найден" println("До сохранения в б.д.") println("${entity1 == entity2}") // true val result = entityAppRepository.save(entity1) println("После сохранения в б.д.") println("${entity1 == result}") // true println("${entity1 == entity2}") // true println(m[entity1] ?: "Не найден") // Найден }
Получается, что написать data перед class для JPA Entity оправдано, если id генерится на стороне приложения.
Теперь рассмотрим случай, когда id генерится на стороне базы данных.
@Table @Entity data class EntityDb( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null, @Column val name: String ) @Test fun `test db generated id`() { val name = "имя" val (entityDb1, entityDb2) = EntityDb(name = name) to EntityDb(name = name) val m = mutableMapOf<EntityDb, String>() m[entityDb1] = "Найден" println("До сохранения в б.д.") println("${entityDb1 == entityDb2}") // true val result = entityDbRepository.save(entityDb1) println("После сохранения в б.д.") println("${entityDb1 == result}") // true println("${entityDb1 == entityDb2}") // false println(m[entityDb1] ?: "Не найден") // Не найден }
Объект оказался изменён после сохранения и поэтому entityDb1 != entityDb2. Автоматически переопределенный метод equals содержал проверку на id == other.id. До сохранения id был null, а после принял значение равное id из базы. entityDb1 добавлен в hashmap, но после сохранения в б.д. hashcode изменился (так как изменился id) и теперь entityDb1 не найти в hashmap после сохранения. Оба метода требуют переопределения и указание data class для JPA Entity избыточно. Кстати, автоматически переопределённый метод toString наглядно демонстрирует side эффект, когда id для объекта не задан до сохранения в базу данных и id получает значение после сохранения в базу данных.
Использовать data class для JPA сущности оправдано, если id записи генерится на стороне приложения и избыточно, если id генерится на стороне базы данных, так как придётся переопределять методы equals и hashcode.
Прошлые записи
- Комната призвания
- Разбираемся с Coroutine в Kotlin - часть четвертая
- Разбираемся с Coroutine в Kotlin - часть третья
- Разбираемся с Coroutine в Kotlin - часть вторая
- Разбираемся с Coroutine в Kotlin - часть первая
- Отпуск длинною в год
- События как источник правды или как я в стартапе участвовал
- Код 2015 против 2023
- Jvm Internals - Перевод
- Мозг против живота или насколько трудно управлять своей жизнью