Simplified Hibernate ORM with Panache and Kotlin
Hibernate ORM 是事实上的标准 Jakarta Persistence(以前称为 JPA)实现,并且在 Java 生态系统中广为人知。带有 Panache 的 Hibernate ORM 在这个熟悉的框架上提供了一个新的层。本指南不会深入探讨这两个框架的具体细节,因为这些细节已经在 Hibernate ORM with Panache guide中介绍过了。在本指南中,我们将介绍在基于 Kotlin 的 Quarkus 应用程序中使用 Panache 与 Hibernate ORM 所需的特定于 Kotlin 的变更。
在使用带有 Panache 的 Hibernate ORM 的 Kotlin 版本时,请注意, |
First: an example
正如我们在带有 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 完全相同方式的静态方法。相反,我们必须使用 a companion object:
@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 pattern
功能移到了 companion object
。除了这一细微改动,我们可以在世界 Java 端轻松映射的方式中使用我们的类型。
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
克隆 Git 存储库: git clone $${quickstarts-base-url}.git
,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。
解决方案位于 hibernate-orm-panache-kotlin-quickstart
directory。
Setting up and configuring Hibernate ORM with Panache and Kotlin
若要开始使用带有 Panache 和 Kotlin 的 Hibernate ORM,您通常可以按照 Java 教程中概述的步骤进行操作。配置您的项目的最大变更包括要包含的 Quarkus 制品。如果您需要,当然可以保留 Java 版本,但是,如果您只需要 Kotlin API,请改为包含以下依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache-kotlin</artifactId> (1)
</dependency>
1 | 请注意末尾添加了 -kotlin 。通常,您只需要此版本,但如果您的项目将使用 Java 和 Kotlin 代码,您可以安全地包含这两个制品。 |
implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin") 1
1 | 请注意末尾添加了 -kotlin 。通常,您只需要此版本,但如果您的项目将使用 Java 和 Kotlin 代码,您可以安全地包含这两个制品。 |
Using the repository pattern
Defining your entity
在使用存储库模式时,您可以将您的实体定义为常规 Jakarta Persistence 实体。
@Entity
class Person {
@Id
@GeneratedValue
var id: Long? = null;
lateinit var name: String
lateinit var birth: LocalDate
lateinit var status: Status
}
Defining your repository
在使用存储库时,您可以通过让它们实现 PanacheRepository
,获得与活动记录模式完全相同的便利方法,并注入到您的存储库中:
@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()
Most useful operations
在编写完存储库后,以下是您将能够执行的最常见操作:
// 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" }
|
如需更多示例,请参考 Java version 了解完整详细信息。两个 API 都是相同的,并且以相同的方式工作,但对 Kotlin 特定的调整除外,这些调整可让事情对 Kotlin 开发者来说更自然。这些调整包括使用 NULL 值更好的方法以及 API 方法中缺少 Optional
。