编辑此页面

简化的 Hibernate ORM with Panache 和 Kotlin

Hibernate ORM 是事实上的 Jakarta Persistence (以前称为 JPA) 标准实现,并且在 Java 生态系统中广为人知。Panache 版本的 Hibernate ORM 在这个熟悉的框架之上提供了一个新的层。本指南不会深入探讨这两者的具体细节,因为它们已经在 Panache 版本的 Hibernate ORM 指南 中进行了介绍。在本指南中,我们将介绍在基于 Kotlin 的 Quarkus 应用程序中使用 Panache 版本的 Hibernate ORM 所需的 Kotlin 特定更改。

当使用 Panache 版本的 Hibernate ORM 的 Kotlin 版本时,请注意,PanacheEntityPanacheQueryPanacheRepository 位于不同的包中:io.quarkus.hibernate.orm.panache.kotlin

首先:一个例子

正如我们在 Panache 版本的 Hibernate 指南中所见,它允许我们通过一些自动提供的功能来扩展实体和存储库(也称为 DAO)的功能。当使用 Kotlin 时,这种方法与我们在 Java 版本中看到的方法非常相似,只有一两个小的变化。要使您的实体启用 Panache,您可以这样定义它:

@Entity
class Person: PanacheEntity() {
    lateinit var name: String
    lateinit var birth: LocalDate
    lateinit var status: Status
}

正如您所见,我们的实体保持简单。但是,与 Java 版本相比有一个细微的差别。Kotlin 语言对静态方法的概念的支持方式与 Java 不同。相反,我们必须使用 伴生对象

@Entity
class Person : PanacheEntity() {
    companion object: PanacheCompanion<Person> {  (1)
        fun findByName(name: String) = find("name", name).firstResult()
        fun findAlive() = list("status", Status.Alive)
        fun deleteStefs() = delete("name", "Stef")
    }

    lateinit var name: String  (2)
    lateinit var birth: LocalDate
    lateinit var status: Status
}
1 伴生对象包含所有与特定实例无关的方法,允许对特定类型进行通用管理和查询。
2 这里有几种选择,但我们选择了 lateinit 方法。这使我们可以将这些字段声明为非空,并知道它们将通过构造函数(未显示)或 Hibernate 从数据库加载数据来正确赋值。
这些类型与这些教程中提到的 Java 类型不同。对于 Kotlin 支持,所有 Panache 类型都将在 io.quarkus.hibernate.orm.panache.kotlin 包中找到。这个子包允许区分 Java 和 Kotlin 变体,并允许在单个项目中不含糊地使用两者。

在 Kotlin 版本中,我们只是将 Active Record 模式 的大部分功能移到了 伴生对象 中。除了这个细微的更改之外,我们就可以以易于从 Java 世界映射的方式来处理我们的类型了。

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或者下载一个 存档

解决方案位于 hibernate-orm-panache-kotlin-quickstart 目录 中。

设置和配置 Panache 版本的 Hibernate ORM 和 Kotlin

要开始使用 Panache 版本的 Hibernate ORM 和 Kotlin,您通常可以遵循 Java 教程中的步骤。配置项目最大的变化是需要包含的 Quarkus artifact。当然,如果您需要,也可以保留 Java 版本,但如果您只需要 Kotlin API,则包含以下依赖项:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm-panache-kotlin</artifactId>  (1)
</dependency>
1 请注意末尾添加了 -kotlin。通常您只需要这个版本,但如果您的项目将同时使用 Java 和 Kotlin 代码,您可以安全地同时包含这两个 artifact。
build.gradle
implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin") (1)
1 请注意末尾添加了 -kotlin。通常您只需要这个版本,但如果您的项目将同时使用 Java 和 Kotlin 代码,您可以安全地同时包含这两个 artifact。

使用存储库模式

定义您的实体

使用存储库模式时,您可以将实体定义为常规 Jakarta Persistence 实体。

@Entity
class Person {
    @Id
    @GeneratedValue
    var id: Long? = null;
    lateinit var name: String
    lateinit var birth: LocalDate
    lateinit var status: Status
}

定义您的存储库

在使用存储库时,通过让它们实现 PanacheRepository,您可以获得与 Active Record 模式完全相同的便捷方法,这些方法已注入到您的存储库中。

@ApplicationScoped
class PersonRepository: PanacheRepository<Person> {
     fun findByName(name: String) = find("name", name).firstResult()
     fun findAlive() = list("status", Status.Alive)
     fun deleteStefs() = delete("name", "Stef")
}

在您的存储库中可以使用 PanacheEntityBase 上定义的所有操作,因此使用它与使用活动记录模式完全相同,只是您需要注入它

@Inject
lateinit var personRepository: PersonRepository

@GET
fun count() = personRepository.count()

最有用的操作

编写存储库后,以下是您可以执行的最常见的操作

// creating a person
var person = Person()
person.name = "Stef"
person.birth = LocalDate.of(1910, Month.FEBRUARY, 1)
person.status = Status.Alive

// persist it
personRepository.persist(person)

// note that once persisted, you don't need to explicitly save your entity: all
// modifications are automatically persisted on transaction commit.

// check if it's persistent
if(personRepository.isPersistent(person)){
    // delete it
    personRepository.delete(person)
}

// getting a list of all Person entities
val allPersons = personRepository.listAll()

// finding a specific person by ID
person = personRepository.findById(personId) ?: throw Exception("No person with that ID")

// finding all living persons
val livingPersons = personRepository.list("status", Status.Alive)

// counting all persons
val countAll = personRepository.count()

// counting all living persons
val countAlive = personRepository.count("status", Status.Alive)

// delete all living persons
personRepository.delete("status", Status.Alive)

// delete all persons
personRepository.deleteAll()

// delete by id
val deleted = personRepository.deleteById(personId)

// set the name of all living persons to 'Mortal'
personRepository.update("name = 'Mortal' where status = ?1", Status.Alive)

所有 list 方法都有等效的 stream 版本。

val persons = personRepository.streamAll();
val namesButEmmanuels = persons
    .map { it.name.toLowerCase() }
    .filter { it != "emmanuel" }
stream 方法需要事务才能工作。

有关更多示例,请参阅 Java 版本 以获取完整详细信息。两个 API 都相同且工作方式相同,除了某些特定于 Kotlin 的调整,旨在使 Kotlin 开发者的体验更加自然。这些调整包括更好地利用可空性以及 API 方法中不使用 Optional

相关内容