The English version of is the official project site. Translated sites are community supported on a best-effort basis.

用Panache和Kotlin简化Hibernate ORM

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.

When using the Kotlin version of Hibernate ORM with Panache, note that the PanacheEntity, PanacheQuery and PanacheRepository are in a different package: io.quarkus.hibernate.orm.panache.kotlin.


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:

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

如你所见,我们的实体仍然很简单。然而,与Java版本有一点不同。Kotlin语言并不像Java那样支持静态方法的概念。相反,我们必须使用一个 companion object(同伴对象)

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 companion object持有所有与特定实例无关的方法,允许进行一般管理和与特定类型绑定的查询。
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.
These types differ from the Java types mentioned in those tutorials. For Kotlin support, all the Panache types will be found in the io.quarkus.hibernate.orm.panache.kotlin package. This subpackage allows for the distinction between the Java and Kotlin variants and allows for both to be used unambiguously in a single project.

在Kotlin版本中,我们只是把大部分的功能 active record pattern 转移到了 companion object 。除了这个细微的变化,我们还可以从 Java 方面轻松映射的方式来处理我们的类型。



克隆 Git 仓库: git clone ,或下载一个 存档

The solution is located in the hibernate-orm-panache-kotlin-quickstart directory.

Setting up and configuring Hibernate ORM with Panache and Kotlin

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:

    <artifactId>quarkus-hibernate-orm-panache-kotlin</artifactId>  (1)
1 注意最后添加了 -kotlin 。通常你只需要这个版本,但如果你的项目将同时使用 Java 和 Kotlin 代码,你可以安全地包含这两个工件。
implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin") (1)
1 注意最后添加了 -kotlin 。通常你只需要这个版本,但如果你的项目将同时使用 Java 和 Kotlin 代码,你可以安全地包含这两个工件。



When using the repository pattern, you can define your entities as regular Jakarta Persistence entities.

class Person {
    var id: Long? = null;
    lateinit var name: String
    lateinit var birth: LocalDate
    lateinit var status: Status


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:

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

PanacheEntityBase 中定义的所有方法都可以在你的Repository类上使用,所以它使用起来与Active Record模式完全一样,只是你需要注入Repository类的实例:

lateinit var personRepository: PersonRepository

fun count() = personRepository.count()



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

// persist it

// 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
    // delete it

// 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

// 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 { }
    .filter { it != "emmanuel" }
stream 方法的运行需要事务。

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.

Related content