简化的 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 版本时,请注意,PanacheEntity 、PanacheQuery 和 PanacheRepository 位于不同的包中: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,则包含以下依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache-kotlin</artifactId> (1)
</dependency>
1 | 请注意末尾添加了 -kotlin 。通常您只需要这个版本,但如果您的项目将同时使用 Java 和 Kotlin 代码,您可以安全地同时包含这两个 artifact。 |
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
。