Criteria Queries and JPA Metamodel with Spring Boot and Kotlin
Tags: hibernate, kotlin, softwaredevelopment, softwareengineering
Published: 2024-01-19 20:52:39

[JPA criteria queries](https://www.baeldung.com/hibernate-criteria-queries) require us to reference entity classes and their attributes. The easiest way to do this is by using _string_s. This means we have to remember the names of the entity attributes when writing a query. But what happens if the name of an entity attribute changes? Well, since we referenced it with a _string_, we have to update it wherever it has been used. This can cause issues, however, when what should be easy refactoring becomes a tedious and error-prone activity.

Luckily for us, this can be avoided. [JPA 2](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#preface) defines a typesafe [Criteria API](https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/Criteria.html) which allows [criteria queries](https://www.baeldung.com/hibernate-criteria-queries) to be constructed by using the so-called [JPA Metamodel](https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/metamodel.html). This feature was introduced to avoid the aforementioned drawback and provide a type-safe and static way to access the metadata of the entity classes. Please note that the task of the metamodel generation can be automated. [JBoss](https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html), [EclipseLink](https://wiki.eclipse.org/UserGuide/JPA/Using_the_Canonical_Model_Generator_%28ELUG%29), [OpenJPA](https://openjpa.apache.org/builds/2.4.1/apache-openjpa/docs/ch13s04.html), [DataNucleus](https://www.datanucleus.org/products/accessplatform_5_2/jpa/query.html#metamodel) are just some of the tools available for the metamodel generation.

In this article, we will see how one of these metamodel generator tools can be integrated into a Spring Boot and Kotlin project.

## Canonical Metamodel

> **The metamodel is a set of objects that describe your domain model**.  
> \[…\]  
> This metamodel is important in 2 ways. First, **it allows providers and frameworks a generic way to deal with an application's domain model**.  
> \[…\]   
> Second, from an application writer's perspective, **it allows very fluent expression of completely type-safe criteria queries**. — [Hibernate Community Documentation](https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/metamodel.html)  
>   
>   
>   

The structure of the metamodel classes is described in the [JPA 2 (JSR 317) specification](https://jcp.org/en/jsr/detail?id=317). If you are interested in understanding in detail how metamodel classes are defined, I recommend reading [this documentation page](https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/metamodel.html) first. Otherwise, feel free to skip to the next chapter.

## Configuring a Metamodel Generation Tool

To generate the metamodel classes, we will use [`hibernate-jpamodelgen`](https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html), the metamodel generator tool provided by [JBoss](https://www.jboss.org/).

First of all, we need to add the `hibernate-jpamodelgen` dependency to our _build.gradle.kts_ file, as well the `kapt`:

```kotlin
plugins {
   kotlin("kapt") version "1.3.72"
}
dependencies {
    implementation ("org.hibernate:hibernate-jpamodelgen:5.4.12.Final")
    
    kapt("org.hibernate:hibernate-jpamodelgen:5.4.12.Final")   
}
```

`_kapt_` is the [Kotlin Annotation Processing Tool](https://kotlinlang.org/docs/reference/kapt.html) and it is required by `hibernate-jpamodelgen` to automatically generate the metamodel classes during build-time.

## JPA Metamodel Classes

To show what a metamodel class looks like, we need an entity class. Since defining JPA entities in Kotlin can be tricky, I recommend reading [this article](https://writech.run/blog/defining-jpa-hibernate-entities-in-kotlin-1ff8ee470805) first.

Let's assume we have already defined the `Author` entity class as follows:

```kotlin
@Entity
open class Author {

    @get:Id
    @get:GeneratedValue
    @get:Column(name = "id")
    open var id: Int? = null

    @get:Column(name = "name")
    open var name: String? = null

    @get:Column(name = "surname")
    open var surname: String? = null

    @get:Column(name = "birth_date")
    @get:Temporal(TemporalType.DATE)
    open var birthDate: Date? = null

    @get:ManyToOne(fetch = FetchType.LAZY)
    @get:JoinColumn(name = "country_id")
    open var country: Country? = null

    @get:ManyToMany(fetch = FetchType.LAZY)
    @get:JoinTable(
            name = "author_book",
            joinColumns = [JoinColumn(name = "author_id")],
            inverseJoinColumns = [JoinColumn(name = "book_id")]
    )
    open var books: MutableSet<Book> = HashSet()
}
```

By default, the corresponding metamodel class will be placed by `kapt` in `build/generated/source/kapt/` followed by the same package as the corresponding entity class.

The metamodel class will be automatically generated during build-time and will have the same name as the entity with an added "\_" at the end. So, the metamodel class generated for the `Author` class will be `Author_` and look like this:

```java
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Author.class)
public abstract class Author_ {

	public static volatile SingularAttribute<Author, Integer> id;
	public static volatile SingularAttribute<Author, String> name;
	public static volatile SingularAttribute<Author, String> surname;
	public static volatile SingularAttribute<Author, Date> birthDate;
	public static volatile SingularAttribute<Author, Country> country;
	public static volatile SetAttribute<Author, Book> books;

	public static final String ID = "id";
	public static final String NAME = "name";
	public static final String SURNAME = "surname";
	public static final String BIRTH_DATE = "birthDate";
	public static final String COUNTRY = "country";
	public static final String BOOKS = "books";
}
```

As you can tell, the generated metamodel class is a Java class. This is not a problem since [**Kotlin is fully interoperable with Java**](https://www.baeldung.com/kotlin-java-interoperability).

## JPA Metamodel In Action

Since the Criteria API provides overloaded methods that accept _String_ references as well as _Attribute_ interface implementations, **we can use the generated metamodel classes in the same way we would use the _String_ references to attributes.** Let's write the criteria query that will fetch all authors named "John".

This is what the criteria query using `Author_` looks like:

```kotlin
// entityManager setup code ...

val criteriaBuilder = entityManager.criteriaBuilder
val criteriaQuery = criteriaBuilder.createQuery(Author::class.java)

// retrieving all authors named "John"
val root = criteriaQuery.from(Author::class.java)
criteriaQuery
    .select(root)
    .where(
        criteriaBuilder.equal(root.get(Author_.name), "John")
    )
val query = entityManager.createQuery(criteriaQuery)

val resultList = query.resultList
```

And this is what it looks like without using the metamodel class:

```kotlin
// entityManager setup code ...

val criteriaBuilder = entityManager.criteriaBuilder
val criteriaQuery = criteriaBuilder.createQuery(Author::class.java)

// retrieving all authors named "John"
val root = criteriaQuery.from(Author::class.java)
criteriaQuery
    .select(root)
    .where(
        // why <String> is required -> https://stackoverflow.com/a/59831558
        criteriaBuilder.equal(root.get<String>("name"), "John")
    )
val query = entityManager.createQuery(criteriaQuery)

val resultList = query.resultList
```

The main difference lies in how the entity attribute _name_ is retrieved. In the first snippet, we used the `Author_.name` reference instead of the conventional column name. If that attribute changes, in the first example we will get a compile-time error, while in the second one, a more dangerous runtime error will be thrown. Using metamodel classes makes the code cleaner, easier, and more robust.

## Conclusion

JPA Metamodel provides a type-safe way to define [criteria queries](https://thorben-janssen.com/criteria-updatedelete-easy-way-to/). This makes future refactorings far easier than referencing the attributes via strings, causing code to be more robust to changes.

Many different metamodel generator tools can be used with an annotation processor to generate the metamodel classes at build time. This means that changes in entity attributes will automatically be reflected in the metamodel classes, avoiding runtime errors.

Thanks for reading! I hope that you found this article helpful.
  
***
_The post "[Criteria Queries and JPA Metamodel with Spring Boot and Kotlin](https://writech.run/blog/criteria-queries-and-jpa-metamodel-with-spring-boot-and-kotlin-9c82be54d626/)" appeared first on [Writech](https://writech.run)._