Kotlin与JPA:如何克服默认构造函数的困境

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

迎接 Kotlin + JPA 中的"默认构造函数地狱"

\n\n

如果你曾尝试让 Kotlin 与 JPA(Java 持久化 API) 协同工作,很可能遇到过 Hibernate 在启动应用程序时抛出类似这样的异常:

\n\n
org.hibernate.InstantiationException: No default constructor for entity: com.example.User\n
\n\n

听起来很熟悉吧?确实如此。这个问题在 Stack Overflow 上积累了成千上万的浏览量,并被冠以了可怕的名称——"Kotlin with JPA: default constructor hell"。

\n\n

在这篇文章中,我们将冷静地、一步一步地拆解:

\n
    \n
  • 这个错误最初发生的原因
  • \n
  • 为什么 Kotlin "不愿意"生成空构造函数
  • \n
  • 三种经过验证的解决问题的方法(从简单到专业)
  • \n
  • 为你的项目选择哪种方法
  • \n
\n\n

我们开始吧!

\n\n

为什么 Hibernate 需要无参构造函数?

\n\n

让我们从 Java 说起。在经典的 Java 中,我们习惯这样写:

\n\n
public class User {\n    private Long id;\n    private String name;\n\n    // 必须的空构造函数\n    public User() {}\n\n    // Getters, setters...\n}\n
\n\n

Hibernate(以及任何 JPA 实现)使用反射来创建对象。当它从数据库中读取一行时,需要创建一个 User 类的实例,然后通过反射填充字段。为此,它需要一个 无参构造函数(默认构造函数)。

\n\n

在 Java 中,如果你没有定义任何其他构造函数,编译器会自动创建一个空构造函数。因此,通常不会出现这个问题。

\n\n

Kotlin 有什么特别之处?

\n\n

Kotlin 是一种非常强调不可变性和安全性的语言。看看一个典型的 Kotlin 类:

\n\n
data class User(\n    val id: Long = 0,\n    val name: String = ""\n)\n
\n\n

这里发生了两件关键的事情:

\n
    \n
  • 所有属性都在 主构造函数 中声明
  • \n
  • 属性被声明为 val(不可变)并具有默认值
  • \n
\n\n

如果主构造函数有参数,Kotlin 不会生成一个单独的空构造函数。即使所有参数都有默认值,在 JVM 字节码层面,这也不被视为一个"真正的"无参构造函数。

\n\n

当 Hibernate 试图通过反射调用 User.class.newInstance() 时,它找不到空构造函数——然后就会崩溃并报错。

\n\n

方法一:最简单——显式添加一个次构造函数

\n\n

最直接的方法是手动添加第二个空构造函数:

\n\n
data class User(\n    val id: Long = 0,\n    val name: String = ""\n) {\n    // 无参次构造函数\n    constructor() : this()\n}\n
\n\n

这里,constructor() : this() 的意思是:"使用默认值调用主构造函数"。

\n\n

优点:

\n
    \n
  • 非常简单明了
  • \n
  • 不需要插件或额外配置
  • \n
\n\n

缺点:

\n
    \n
  • 你需要为每个实体编写这个构造函数
  • \n
  • 如果你有 10-15 个实体,这会变得很繁琐
  • \n
  • 容易忘记添加
  • \n
\n\n

方法二:使用 open 类并将所有属性设为 var

\n\n

网上很多例子建议使用这个解决方案:

\n\n
open class User {\n    var id: Long = 0\n    var name: String = ""\n}\n
\n\n

这里,我们将类设为 open(这样 Hibernate 就能创建代理),并将属性设为 var(可变的)。而且没有主构造函数——这意味着 Kotlin 会生成一个默认的空构造函数。

\n\n

优点:

\n
    \n
  • 非常简单且无需额外样板代码
  • \n
  • 完全符合 JPA 要求
  • \n
\n

博客

书籍推荐