Did you know that the Helidon project has full, managed JPA 2.2 support?
This is so much more than the simplistic Java SE-mode Java Persistence API you are probably used to. You may be used to calling
Persistence.createEntityManagerFactory() and then managing
EntityManagers yourself, taking care to handle exceptions and thread safety and transactions and rollbacks and a lot of other headaches yourself as well. That’s no fun.
Instead, with Helidon MicroProfile, all of those problems are managed for you. You work with Helidon MicroProfile’s managed JPA support in the same convenient, mostly declarative way that you would if you were writing code for a Java EE application server. But Helidon MicroProfile is smaller, lighter and faster than the application servers of yore—in addition to not being an application server at all! This is the best of both worlds.
In this article, we’ll add JPA features to a standard Helidon MicroProfile example project and show you how to work with it in the familiar Java EE-like way you’re used to. I’ve added lots of background links in case you want to check out the primary sources. And we’ll go into enough detail along the way that hopefully you’ll understand how all the pieces hang together.
Let’s dive in.
Creating the Project
First, we’ll create a new Maven project using a Maven archetype. For this example, I’ll assume you have a JDK of version 11 and a Maven installation of version 3.6.3.
cd into a directory where you want your project to be created and type:
This uses one of the Helidon archetypes to create a simple Helidon MicroProfile-based project named
helidon-jpa. Throughout the course of this article, we’ll augment this project to use Helidon MicroProfile’s managed JPA support.
Helidon MicroProfile’s managed JPA support is implemented as a set of loosely coupled components, not a monolithic slab. You grab only what you need, put it into your Maven project, and at the end of the day you have a custom-tailored, lean environment for running your JPA application. So the first thing is to make sure your new project knows about the components that will collaborate to give you JPA functionality.
JPA itself is basically component-oriented. Database drivers are managed by connection pools, which are managed by a transaction engine, which
EntityManagers cooperate with to manage database updates. Helidon MicroProfile lets you plug in the components you need to manifest these concepts. Let’s look at each of them in order.
The first component you’ll need is a JDBC-compliant database driver. For this example, we’ll use the H2 database driver. Place this in your
pom.xml file in the
Now your project knows about the H2 database.
If you’re working with another database, then use that database’s driver jar instead.
The next component you’ll need is a database connection pool. Helidon MicroProfile comes with support for a couple of connection pools: HikariCP and Oracle’s Universal Connection Pool. In this example, we’ll choose the HikariCP connection pool. Place this in your
pom.xml file in the
Now your project knows how to pool connections to your database.
The next component that you’ll add is a Java Transaction API (JTA)-compliant transaction manager. Helidon has support for the Narayana transaction engine. This component will allow your JPA application to not have to worry about setting up and tearing down transactions manually, or doing the right thing when rollbacks occur. Place this in your
pom.xml in the
Now your project knows how to enroll pooled database connections (from the driver you just installed above, managed by the connection pool you just installed above) into automatically managed JTA transactions. Nice.
Helidon’s Managed JPA Support
The next component is Helidon MicroProfile’s JPA support: the glue code that binds a JPA provider to the connection pool and the transaction manager. Now that you’ve added implementations of those components above, you can now add Helidon MicroProfile’s JPA support on top of them. Place this in your
pom.xml in the
Now your project knows about JPA constructs like
You haven’t yet, however, picked a JPA provider to use to provide an implementation of them.
Unsurprisingly, the last component in the runtime puzzle is the JPA provider itself. You can choose from EclipseLink or Hibernate. In this example, we’ll use EclipseLink. Place this in your
pom.xml in the
Now your project will use EclipseLink to implement things like
Helidon MicroProfile’s EclipseLink integration is slightly devious: it makes EclipseLink think that it is running in a traditional Java EE application server, even though of course it is not, so almost everything you can do with JPA in that kind of environment you can now do with it in this simple, microservices-oriented environment.
So far we’ve added the runtime components that enhance the base Helidon MicroProfile server so that it knows about JPA. Now you need to add some APIs that will let you actually write and compile code that will use the JPA support.
JPA and JTA APIs
First, you’ll need to make sure your project knows about the provider-independent JPA and JTA APIs themselves. You’ve added the runtime components that implement these APIs above, but if you try to write code that uses things like
EntityManager right now your code won’t compile. To actually make it all work, place this in your
pom.xml in the
Now you can write and compile code that refers to things like
javax.transaction.Transactional. Note that the Maven
scope in this case is
provided: EclipseLink will make the implementation of the JPA APIs available at runtime; Helidon MicroProfile’s Narayana integration (that you added earlier) will make the implementation of the JTA APIs available at runtime. Here, you make them available at compile time, but you tell the system that these APIs will be provided by some runtime component at runtime.
Configuring The Project
Now that you’ve set up the project, it’s time to get some configuration squared away so that the components you’ve installed will know how to reach the database that will back your JPA project.
The first thing we’ll do is create a DDL script that creates some tables in the database, so that when your application talks to the database there’s some structured data in it. JPA itself offers some features for this, but for this example we will not use them, and, instead, will keep things simple and transparent.
Create a file named
src/main/resources/greeting.ddl with these contents:
This will, if executed against an H2 database, create a simple table named
GREETING with two columns:
RESPONSE. It will also ensure that there’s at least a single row in that table. (The H2 constructs like
IF NOT EXISTS and
MERGE INTO ensure that you can run this DDL several times without failing and without damaging an already created schema.)
Finally, please note that managing database schema upgrades in a real application is a thorny subject worthy of many articles and books, and that using a simple DDL file like we do here is totally inadequate for production situations. (But it’s easy to use in a tutorial-style blog article!)
Configuring the HikariCP Connection Pool
Helidon MicroProfile at the lowest level doesn’t care where its configuration comes from. That means you have an almost infinite number of ways you can get configuration into your application. To keep things simple, we’re going to use one of the configuration sources that Helidon knows about by default:
application.yaml. Create a file named
src/main/resources/application.yaml with the following contents:
(The first two lines of this file are webserver-related and don’t concern Helidon MicroProfile’s managed JPA support. They’ll make your web application bind to port
The configuration under the
DataSource YAML structure tells the connection pool how to make, unsurprisingly,
javax.sql.DataSource instances. In this case, we have a recipe that says how to make a
javax.sql.DataSource instance named
The information under that is subject to the rules of the HikariCP connection pool, and you can read all about it on its website. Briefly, with the
dataSourceClassName property we say what database driver-supplied
DataSource class to use as the foundation (
org.h2.jdbcx.JdbcDataSource), and then, under the
dataSource heading, we specify various properties to set on it. Here we specify the
Let’s look at the
url property. This JDBC-compliant URL property is subject to the rules of the H2 database, and you can read all about it on its website. Briefly, here we say we want an in-memory H2 database (
:greeting), and we run the DDL script created above when the database comes up.
Now that you’ve told the connection pool how to pool connections to your database and how to appear inside of Helidon MicroProfile as a
javax.sql.DataSource instance, you will tell JPA how to find and use it, and how to work with your Java entity classes (that you’ll write in a bit).
JPA looks for a classpath resource named
META-INF/persistence.xml to understand how it is supposed to behave. That file can take a couple of different forms, and they are often a source of confusion for Java developers.
The first form is for developers who want to manage
EntityManagers themselves, and transactions themselves, and thread safety themselves, and exception handling themselves, and other annoying concerns (hint: that’s not you). It involves specifying database connectivity directly in the
persistence.xml file, and avoiding the usage of various JTA-oriented constructs. Helidon MicroProfile’s JPA support does not use this form. I mention it here only because it often shows up in bad JPA examples you may find elsewhere on the web.
The second form is for developers who want to use a JPA implementation that is managed for them, like Helidon MicroProfile’s managed JPA support (hint: that’s you!). It specifies the usage of JTA and pushes the concerns of how to connect to a database off onto a connection pool implementation (which you just set up earlier). Helidon MicroProfile’s managed JPA support uses this form.
In a managed JPA environment such as that offered by Helidon MicroProfile, fundamentally all you have to do is tell JPA where to find a named data source that can be enrolled in JTA transactions. Then the named data source worries about how to talk to the database. In Java EE, this name used to be a JNDI name (often starting with
java:comp/env/jdbc) but it’s much simpler in Helidon MicroProfile. It’s any name you want, so long as it identifies a data source that you configured.
To belabor the point: you do not tell JPA details about your database connectivity. You already did that when you edited the
application.yaml file, so it has no place here. Instead, you give JPA the name of the data source you configured earlier so that the runtime can find it in some way.
greetingDataSource you configured earlier? There’s your data source name.
So create a file named
src/main/resources/META-INF/persistence.xml with the following contents:
You’ll notice that the bulk of this file is XML boilerplate. Its actual contents are fairly simple:
persistence-unitwe’re configuring here is named
greeting, and uses JTA for transaction management.
<jta-data-source>element says that the name of the data source that JPA will use is
greetingDataSourceand you just set that up earlier in this article. This is where the linkage is made between JPA and your connection pool.
<class>element you see designates what Java classes are JPA entities that JPA should know about. We’ll write one shortly.
<properties>stanza lets a JPA implementation be configured with vendor-specific properties. Here, we are giving EclipseLink some hints about what it can expect when it starts up (it should deploy early, it is targeting an H2 database, it should log using Java’s native logging facilities, and so on). The only EclipseLink-specific
<property>here that is required for EclipseLink to function properly with Helidon’s managed JPA support is the
eclipselink.weavingproperty. It must be set to
false. We’ll discuss (build-time) weaving below.
Finally, there are real-world concerns involving where you should put your
META-INF/persistence.xml in a real-world application. Should it go in the same project alongside your JPA entities? What if you have several staging environments? What about unit testing? This article deliberately does not go into these issues.
Helidon MicroProfile uses Java’s native logging facilities. For convenience, go ahead and add
src/main/resources/logging.properties and fill it with the following contents:
This will allow you to see the connection pool working, the JAX-RS machinery coming up and some other useful things when you start your application. Helidon picks it up automatically.
Now that you have configured your database connection pool, and configured JPA to know about it, and set up all the runtime and API componentry, you can write some code!
The Entity Class
First, let’s write a JPA entity class.
Create a file named
This is a JPA entity class. I’ve made all of the often implicit defaults as explicit as I can for maximum transparency.
You can hopefully therefore see that this class defines an entity, whose JPA-related state is accessed by the JPA provider directly from its fields (not from, say, its “getter” and “setter” methods). It is mapped to the
GREETING table. Its identifier is its
salutation field (you may recall the
SALUTATION column from when you created the DDL script above; you can see that the
salutation field is mapped to it). Going into the various issues around designing entities is beyond the scope of this article, so we’ll leave it at this.
A Brief Digression On Build-Time Weaving
EclipseLink, like all JPA providers, expects to be able to modify the bytecode of your entity classes so that it can transparently (to you) figure out what fields have been set on them (among other reasons). Otherwise you would have to track this yourself, by hand, and that’s no fun at all. This bytecode modification is known as weaving.
Weaving can be done “statically” at build time, or “dynamically” at runtime. To cut to the chase, because Helidon MicroProfile is not a heavyweight Java EE application server with multiple classloaders and a deployment phase, dynamic runtime weaving in the way that is spelled out in the JPA specification is practically speaking not possible.
Fortunately, build-time weaving is what you always want to do anyway—for many reasons, ranging from performance to classloading issues. So you need to add some stuff to your
pom.xml to set up build-time weaving.
Here, we add a
<plugin> that tells EclipseLink to perform such weaving at build time. (This stanza also creates the static JPA metamodel classes at build time.)
Inject a Container-Managed
Your project is set up, your database is ready, your configuration is all set, you have a JPA entity that will be compiled and woven at build time—now it’s finally time to start using all this stuff!
ExampleResource.java file in
src/main/java/io/helidon/example/jpa so that there are a few extra imports. You’ll use all of them:
Then annotate the resource itself with a scope annotation to make sure it gets discovered and processed:
Next, add an injected
EntityManager. Just as you would do in Java EE, you use the
Easy. And familiar, if you’ve ever worked in Java EE before.
Now add a JPA-related resource method:
Note the use of
@Transactional here: This will cause a JTA transaction to automatically start before this method is executed, and it will be committed at the end. If a problem occurs, the transaction will roll back. The injected
EntityManager will participate automatically. Nice.
Building, Running and Vetting the Application
You’ve set up your database, configured your connection pool, told JPA how to talk to it, created your entities and written your code. Let’s build the application.
cd to the top level directory and run
If you’ve done everything right so far, you can now run the application by typing
java -jar target/helidon-jpa.jar.
Finally, you can test that all the pieces are working together by typing
curl http://localhost:8080/example/response/Marco. You should get
Polo back as a response.
Summary and Takeaway
Helidon MicroProfile’s JPA support goes beyond the usual surface-level integration to give you the kind of managed support you are probably used to from the Java EE application server days. It does it transparently and using familiar tools.
I hope you enjoyed this article. I plan on writing more about some of the more advanced features of the Helidon MicroProfile managed JPA support in the future.