Introducing Jakarta Data in WildFly Preview

I’m excited that in the 34 Beta release we were able to introduce support for Jakarta Data into WildFly Preview. It was a bit of an unexpected last minute thing that we were able to do this, which left us without time to much in the way of documentation. We’ll correct that for WildFly 35, but in the meantime I’ll use this blog post as a way to introduce the basics.

Note
In the 34 release, Jakarta Data is only available in WildFly Preview, and not in standard WildFly. It is provided at the preview stability level, which is enabled out-of-the-box in WildFly Preview.

Jakarta Data Overview

My purpose in this post isn’t to dive much into the details of Jakarta Data itself; there are other resources that do a good job of covering that. I want to focus here on how the WildFly Preview and Hibernate ORM integration of Jakarta Data works, so users can get going with using Jakarta Data in a WildFly server. So this next bit is very brief and high level.

Jakarta Data brings the repository pattern to the Jakarta ecosystem. As explained in the Jakarta Data 1.0 Specification

a repository is a mediator between an application’s domain logic and the underlying data storage, be it a relational database, NoSQL database, or any other data source.

In Jakarta Data, a Repository provides a structured and organized way to interact with data. It abstracts data storage and retrieval complexities, allowing you to work with domain-specific objects and perform common operations on data without writing low-level database queries.

An application developer defines a repository by providing an interface annotated with the Jakarta Data @Repository annotation. The repository interface declares methods used for data retrieval and modification of a particular entity type. A repositoy interface can include different methods that deal with different entity types, giving application authors flexibility to define repositories that fit the needs of their application domain.

Following is an example repository:

@Repository
interface Publishing {
    @Find
    Book book(String isbn);

    @Find
    Author author(String ssn);

    @Insert
    void publish(Book book);

    @Insert
    void create(Author author);

    // query methods
    ...
}

Book and Author are typical entity classes.

The repository interface methods are annotated with various Jakarta Data annotations (@Insert, @Find, etc) that define the expected persistence behavior of the method.

There’s much more to the Jakarta Data programming model than this; for all the details see:

A Jakarta Data implementation like WildFly Preview can support one or more Jakarta Data providers. A provider understands one or more Java annotation types that are used to define entities, and it understands how to interact with a particular type of back end datastore.

WildFly Preview’s Jakarta Data implementation supports the Hibernate Data Repositories provider, which uses Hibernate ORM to interact with a variety of different relational databases. Hibernate Data Repositories supports the jakarta.persistence.Entity annotation as the mechanism for application authors to define entities.

Using Hibernate Data Repositories in Your Application

There are two key things to understand in order to use WildFly Preview’s Hibernate Data Repositories provider:

  • How to configure build time generation of the implementation of your @Repository interfaces.

  • How to configure the runtime behavior of the Hibernate ORM instance that will interact with the database.

Build-time Generation of Repository Implementations

An application author using Jakarta Data simply writes an interface for their repository, but of course for that to work at runtime there must be an actual implementation of that interface. It’s the responsibility of the Jakarta Data provider to provide that implementation. Hibernate Data Repositories does this by generating the implementation classes as part of the build of your application.

So, to use Jakarta Data with WildFly Preview you need to configure the generation of those classes as part of your application build. In a Maven build this is done by configuring the Maven compiler plugin to use the org.hibernate.orm:hibernate-jpamodelgen artifact as an annotation processor:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <!-- 3.12 or later is necessary for proper dependency management in annotationProcessorPaths -->
                <version>3.12.1</version>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.hibernate.orm</groupId>
                            <artifactId>hibernate-jpamodelgen</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

Note that there is no version element in the org.hibernate.orm:hibernate-jpamodelgen declaration above. You could provide one, but best practice is to control the version in your pom’s dependencyManagement. Importing the org.wildfly.bom:wildfly-ee-preview-with-tools BOM lets you align the version of Hibernate artifacts with what’s used in your target WildFly Preview runtime:

    <dependencyManagement>
        <dependencies>
            <!-- importing the ee-with-tools BOM adds specs and other useful artifacts as managed dependencies -->
            <dependency>
                <groupId>org.wildfly.bom</groupId>
                <artifactId>wildfly-ee-preview-with-tools</artifactId>
                <version>34.0.0.Beta1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
Warning
Some users may have learned to configure Hibernate annotation processing by declaring org.hibernate.orm:hibernate-jpamodelgen as a provided dependency in their pom. With the Hibernate version used with WildFly, this will likely fail. Use the maven-compiler-plugin configuration approach described above.

If you’re using Gradle, you’ll need to use annotationProcessor:

annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen:6.6.1'

The generated repository implementation classes internally use various Hibernate ORM classes, so to compile the generated code you’ll need to add a dependency on Hibernate:

  <dependencies>
      <dependency>
          <groupId>org.hibernate.orm</groupId>
          <artifactId>hibernate-core</artifactId>
          <scope>provided</scope>
      </dependency>
  </dependencies>

Configuring Hibernate ORM

Under the covers, your repository implementation will use Hibernate ORM to interact with the database. You configure ORM by providing a META-INF/persistence.xml file, the same as you would with a Jakarta Persistence application:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="3.0">

    <persistence-unit name="Publisher">

        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="create"/>
            <property name="hibernate.show_sql" value="false"/>
        </properties>

    </persistence-unit>

</persistence>

The jta-data-source value should match the value of the jndi-name attribute in a datasource you’ve declared in the WildFly Preview datasources or datasources-agroal subsystem configuration.

Configuring WildFly Preview to Support Jakarta Data

Jakarta Data in WildFly Preview is configured using the new jakarta-data subsystem. This subsystem isn’t included in any of WildFly Preview’s out-of-the-box configuration files, so to use it you’ll need to add it to your configuration.

If you’re using a complete WildFly Preview installation, like the ones available from the WildFly downloads page, then you can use the JBoss CLI to add the Jakarta Data extension and subsystem to your configuration:

$ /extension=org.wildfly.extension.jakarta.data:add
$ /subsystem=jakarta-data:add

If you’re using Galleon to provision a slimmed WildFly Preview installation, you’ll need to specify the jakarta-data Galleon layer. For example if you are using the WildFly Maven Plugin to provision a server that supports a Jakarta REST application interacting with a Postgresql database, the configuration in your application’s pom.xml might look like this:

    <build>
        <plugins>
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                 <artifactId>wildfly-maven-plugin</artifactId>
                 <configuration>
                     <feature-packs>
                         <feature-pack>
                             <location>wildfly-preview@maven(org.jboss.universe:community-universe)</location>
                         </feature-pack>
                         <feature-pack>
                             <groupId>org.wildfly</groupId>
                             <artifactId>wildfly-datasources-preview-galleon-pack</artifactId>
                             <version>8.0.1.Final</version>
                         </feature-pack>
                     </feature-packs>
                     <layers>
                         <layer>jaxrs-server</layer>
                         <layer>jakarta-data</layer>
                         <layer>postgresql-datasource</layer>
                      </layers>
                  </configuration>
                  <executions>
                      <execution>
                          <goals>
                              <goal>package</goal>
                          </goals>
                      </execution>
                  </executions>
              </plugin>
        </plugins>
    </build>

The subsystem itself is very simple and doesn’t expose any configuration attributes.

Note that for the jakarta-data subsystem to work, the jpa subsystem must be present in your configuration. It’s present in our out-of-the-box configurations and will be included if you provision a server using the jakarta-data Galleon layer.

Please try out Jakarta Data in WildFly Preview and give us your feedback! We’ll continue to work on the integration, with a goal of including it in standard WildFly in one of the next couple of releases.