Наследство котлина и JPA - PullRequest
1 голос
/ 02 мая 2019

Я пытаюсь реализовать наследование с Kotlin и JPA. Мой абстрактный базовый класс (помеченный @Entity) содержит идентификатор (помеченный @Id и @GeneratedValue) и другие метаданные, такие как createDate и т. Д. Я получаю несколько ошибок из Hibernate, по одной для каждого поля, кроме идентификатора :

org.hibernate.tuple.entity.PojoEntityTuplizer - HHH000112: Получатели ленивых классов не могут быть окончательными: com.example.BaseEntity.createDate

Поскольку я прочитал, мне нужно включить ключевое слово open для каждого свойства.

У меня есть 3 вопроса по этому поводу:

  1. Почему я должен делать это в суперклассе, а не в подклассе? Я не переопределяю эти свойства.
  2. Почему он не жалуется на удостоверение личности?
  3. Кажется, он работает без ключевого слова open, тогда почему регистрируется ошибка?

Edit:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
abstract class BaseEntity(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0,
    val createdAt: Instant = Instant.now()
)

@Entity
class SubClass(
    val someProperty: String = ""
) : BaseEntity()

Я использую плагин JPA для Gradle, который, как мне кажется, создает конструктор noarg, поэтому мне не нужно указывать все, что можно обнулять.

Спасибо!

1 Ответ

1 голос
/ 02 мая 2019

Зарегистрированная ошибка связана с отложенная загрузка .

Hibernate extends сущностей во время выполнения, чтобы включить его. Это делается путем перехвата доступа к свойствам, когда объект загружается лениво.

Котлин перевернул правила, и все классы там по умолчанию final. Именно поэтому мы рекомендуем добавить ключевое слово open.

Если свойство не open, hibernate не может перехватить доступ к нему, потому что методы final не могут быть переопределены. Отсюда и ошибка.

Почему он не жалуется на удостоверение личности?

Потому что @Id всегда загружен. Нет необходимости перехватывать доступ к нему.

Кажется, он работает без ключевого слова open, тогда почему регистрируется ошибка?

Ключевое слово здесь кажется . Это может привести к тонким ошибкам.

Рассмотрим следующее @Entity:

@Entity
public class Book {
  @Id
  private Long id;
  private String title;

  public final Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public final String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }
}

И @Test:

@Test
public void test() {
  EntityManager entityManager = entityManagerFactory.createEntityManager();
  entityManager.getTransaction().begin();
  // signal here
  Book book = new Book();
  book.setId(1L);
  book.setTitle("myTitle");  
  entityManager.persist(book);
  // noise
  entityManager.getTransaction().commit();
  entityManager.close();

  entityManager = entityManagerFactory.createEntityManager();
  entityManager.getTransaction().begin();
  // signal
  Book reference = entityManager.getReference(Book.class, 1L);
  String title = reference.getTitle();
  assertNull(title); // passes

  entityManager.getTransaction().commit();
  entityManager.close();
}

Этот тест проходит , но не должен (и не проходит, если getTitle не final). Это было бы трудно заметить

Почему я должен делать это в суперклассе, а не в подклассе? Я не переопределяю эти свойства.

Похоже, Hibernate сдает , когда видит final @Entity.

Добавьте open к SubClass и вы получите драгоценное:

2019-05-02 23:27:27.500 ERROR 5609 --- [           main] o.h.tuple.entity.PojoEntityTuplizer      : HHH000112: Getters of lazy classes cannot be final: com.caco3.hibernateanswer.SubClass.someProperty 

См. Также :

PS

Вы забыли включить @MappedSuperclass в BaseEntity?

Без аннотации он должен потерпеть неудачу с чем-то вроде:

org.hibernate.AnnotationException: No identifier specified for entity: com.caco3.hibernateanswer.SubClass
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...