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 的变更。
Hibernate ORM is the de facto standard Jakarta Persistence (formerly known as JPA) implementation and is well-known in the Java ecosystem. Hibernate ORM with Panache offers a new layer atop this familiar framework. This guide will not dive in to the specifics of either as those are already covered in the Hibernate ORM with Panache guide. In this guide, we’ll cover the Kotlin specific changes needed to use Hibernate ORM with Panache in your Kotlin-based Quarkus applications.
在使用带有 Panache 的 Hibernate ORM 的 Kotlin 版本时,请注意, |
When using the Kotlin version of Hibernate ORM with Panache, note that the |
First: an example
正如我们在带有 Panache 的 Hibernate 指南中所看到的,它允许我们用一些自动提供的功能扩展实体和存储库(也称为 DAO)中的功能。在使用 Kotlin 时,该方法与 Java 版本中的方法非常相似,只有一两个小改动。如需启用 Panache 的实体,您可以将其定义为类似于以下内容:
As we saw in the Hibernate with Panache guide, it allows us to extend the functionality in our entities and repositories (also known as DAOs) with some automatically provided functionality. When using Kotlin, the approach is very similar to what we see in the Java version with a slight change or two. To Panache-enable your entity, you would define it something like:
@Entity
class Person: PanacheEntity() {
lateinit var name: String
lateinit var birth: LocalDate
lateinit var status: Status
}
如您所见,我们的实体仍然简单。但是,与 Java 版本相比,有一个细微的差别。Kotlin 语言不支持与 Java 完全相同方式的静态方法。相反,我们必须使用 a companion object:
As you can see our entities remain simple. There is, however, a slight difference from the Java version. The Kotlin language doesn’t support the notion of static methods in quite the same way as Java does. Instead, we must use 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 | The companion object holds all the methods not related to a specific instance allowing for general management and querying bound to a specific type. |
2 | Here there are options, but we’ve chosen the lateinit approach. This allows us to declare these fields as non-null
knowing they will be properly assigned either by the constructor (not shown) or by hibernate loading data from the
database. |
这些类型与那些教程中提到的 Java 类型不同。对于 Kotlin 支持,所有 Panache 类型都将在 `io.quarkus.hibernate.orm.panache.kotlin`包中找到。此子包允许区分 Java 和 Kotlin 变体,并允许在单个项目中明确使用这两个变体。 |
These types differ from the Java types mentioned in those tutorials. For Kotlin support, all the Panache
types will be found in the |
在 Kotlin 版本中,我们只是将大部分 active record pattern
功能移到了 companion object
。除了这一细微改动,我们可以在世界 Java 端轻松映射的方式中使用我们的类型。
In the Kotlin version, we’ve simply moved the bulk of the active record pattern
functionality to the companion object
. Apart from this slight change, we can then work with our types in ways that map easily
from the Java side of world.
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
解决方案位于 hibernate-orm-panache-kotlin-quickstart
directory。
The solution is located in the 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,请改为包含以下依赖项:
To get started using Hibernate ORM with Panache and Kotlin, you can, generally, follow the steps laid out in the Java tutorial. The biggest change to configuring your project is the Quarkus artifact to include. You can, of course, keep the Java version if you need but if all you need are the Kotlin APIs then include the following dependency instead:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache-kotlin</artifactId> (1)
</dependency>
1 | Note the addition of -kotlin on the end. Generally you’ll only need this version but if your project will be using
both Java and Kotlin code, you can safely include both artifacts. |
implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin") 1
1 | Note the addition of -kotlin on the end. Generally you’ll only need this version but if your project will be using
both Java and Kotlin code, you can safely include both artifacts. |
Using the repository pattern
Defining your entity
在使用存储库模式时,您可以将您的实体定义为常规 Jakarta Persistence 实体。
When using the repository pattern, you can define your entities as regular Jakarta Persistence entities.
@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
,获得与活动记录模式完全相同的便利方法,并注入到您的存储库中:
When using Repositories, you get the exact same convenient methods as with the active record pattern, injected in your Repository,
by making them implement 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`中定义的所有操作都可以在您的存储库中使用,因此使用它与使用主动纪录模式完全相同,除了您需要注入它:
All the operations that are defined on PanacheEntityBase
are available on your repository, so using it
is exactly the same as using the active record pattern, except you need to inject it:
@Inject
lateinit var personRepository: PersonRepository
@GET
fun count() = personRepository.count()
Most useful operations
在编写完存储库后,以下是您将能够执行的最常见操作:
Once you have written your repository, here are the most common operations you will be able to perform:
// 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
版本。
All list
methods have equivalent stream
versions.
val persons = personRepository.streamAll();
val namesButEmmanuels = persons
.map { it.name.toLowerCase() }
.filter { it != "emmanuel" }
|
The |
如需更多示例,请参考 Java version 了解完整详细信息。两个 API 都是相同的,并且以相同的方式工作,但对 Kotlin 特定的调整除外,这些调整可让事情对 Kotlin 开发者来说更自然。这些调整包括使用 NULL 值更好的方法以及 API 方法中缺少 Optional
。
For more examples, please consult the Java version for complete details. Both APIs
are the same and work identically except for some Kotlin-specific tweaks to make things feel more natural to
Kotlin developers. These tweaks include things like better use of nullability and the lack of Optional
on API
methods.