Kotlin继承和JPA

时间:2019-05-02 19:00:07

标签: jpa inheritance kotlin spring-data-jpa

我正在尝试通过Kotlin和JPA实现继承。我的抽象基类(用@Entity注释)包含ID(用@Id@GeneratedValue注释)和其他元数据,例如createDate等。我从Hibernate中遇到了几个错误,一个是对于除ID以外的每个字段:

  

org.hibernate.tuple.entity.PojoEntityTuplizer-HHH000112:惰性类的获取器不能是最终的:com.example.BaseEntity.createDate

如我所读,我需要为每个属性都包含open关键字。

我对此有3个问题:

  1. 为什么在超类中必须这样做,而在子类中则不需要?我没有覆盖这些属性。
  2. 为什么不抱怨ID?
  3. 没有open关键字似乎可以正常工作,那么为什么记录错误?

编辑:

@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()

我正在为Gradle使用JPA插件,我相信它会创建noarg构造函数,这就是为什么我不必指定所有可为空的东西的原因。

谢谢!

1 个答案:

答案 0 :(得分:1)

记录的错误与lazy loading有关。

在运行时休眠extends实体以启用它。通过延迟加载实体时拦截对属性的访问来完成此操作。

科特林(Kotlin)违反了规则,默认情况下所有类都是final。这就是我们建议添加一个open关键字的原因。

如果属性不是open,则休眠状态无法拦截对它的访问,因为final方法不能被覆盖。因此是错误。

  

为什么不抱怨ID?

因为总是加载@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,则会失败)。 这很难注意到

  

为什么在超类中必须这样做,而在子类中则不需要?我没有覆盖这些属性。

看起来像休眠的放弃时,它会看到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