Persistence
Overview
Helidon MP comes with deep integration for three specification-defined, broadly persistence-related technologies that can be used together or separately:
Each integration’s setup, configuration, and usage are described below.
Named Data Source Integration
Overview
Helidon MP’s named data source integration allows you to safely inject managed
javax.sql.DataSource instances that are annotated with
jakarta.inject.Named annotations into your Helidon MP
application. java.sql.Connection objectsacquired from these data sources will be pooled by your choice of
one of two possible connection pool implementations.
The connections managed by the connection pool will be supplied by your relational database vendor’s JDBC driver.
How you set up Helidon MP’s named data source integration differs depending on which of these two connection pools, which JDBC driver, and which relational database product you use.
Representative setups are described below. This list of setups is not exhaustive.
Project Setup
Setting Up a Connection Pool
Overview
Helidon MP’s named data source integration requires a connection pool implementation.
Helidon MP comes with support for two connection pools:
You can choose to use either, but not both.
Details concerning each connection pool’s setup are described below.
Setting Up the HikariCP Connection Pool
Maven Coordinates (HikariCP)
To include the HikariCP connection pool in your Helidon MP application:
- Ensure your dependencies are managed
- Ensure the following
<dependency>element is present as a child element of your project’spom.xmlfile’s<dependencies>element:
Setting up the Oracle Universal Connection Pool
Maven Coordinates (Oracle Universal Connection Pool)
To include the Oracle Universal Connection Pool in your Helidon MP application:
- Ensure your dependencies are managed
- Ensure the following
<dependency>element is present as a child element of your project’spom.xmlfile’s<dependencies>element:
Setting Up a Database Driver
Overview
Regardless of which connection pool you use, at the lowest level, JDBC database driver classes are what is ultimately responsible for making any connections to a relational database. JDBC database driver classes are database-product-specific.
Once you have decided upon a relational database product to use, and JDBC driver
classes to use to connect to it, ensure your dependencies are
managed, and then ensure that a runtime-scoped
<dependency> element describing your JDBC driver classes is present as a child
element of your project’s pom.xml file’s <dependencies> element.
See the JDBC 4.3 Specification for more information about JDBC.
Representative setups are described below. This list of setups is not exhaustive.
Setting Up H2
Maven Coordinates (H2)
To include the H2 JDBC driver classes in your Helidon MP application so your application can connect to an H2 database (whether in-memory or persistent):
- Ensure your dependencies are managed
- Ensure the following
<dependency>element is present as a child element of your project’spom.xmlfile’s<dependencies>element:
Setting Up Oracle JDBC
Maven Coordinates (Oracle JDBC)
To include the Oracle JDBC driver classes in your Helidon MP application so your application can connect to an Oracle database:
- Ensure your dependencies are managed
- Read and understand Developer’s Guide For Oracle JDBC 21c on Maven Central
- For a basic setup, ensure the following
<dependency>element is present as a child element of your project’spom.xmlfile’s<dependencies>element:
Configuration
Overview
Each connection pool supported by Helidon’s named data source integration
support is, itself, a DataSource that wraps a vendor-supplied DataSource
present in the JDBC driver classes you added to your project. You must configure
both the pool and the vendor-supplied DataSource.
To configure Helidon MP’s named data source integration:
- Decide where each property of the configuration will reside, as permitted by Helidon MP’s MicroProfile Config implementation
- Create configuration suitable for the combination of your selected
connection pool and your selected vendor-supplied
DataSourceimplementation in those locations
Helidon MP’s named data source integration relies on Helidon MP’s usage of MicroProfile Config, so you have many choices for each configuration property when deciding on your configuration’s location in (1) above.
The configuration property values themselves are necessarily specific to the
connection pool you selected, and to the vendor-supplied DataSource
responsible for actually connecting to your relational database. In general, at
a minimum, in your configuration you typically supply:
- Information so the connection pool knows which vendor-supplied
DataSourceimplementation to manage - A JDBC URL specific to the vendor-supplied
DataSourcedescribing where the database is located, so the managed vendor-suppliedDataSourceknows how to connect to it - Information required for the vendor-supplied
DataSourceto authenticate to the database and otherwise tailor itself to it
Some examples for representative configurations follow. This list of configurations is not exhaustive.
Configuration Prefixes
All MicroProfile Config-compatible property names for Helidon MP’s named data source integration follow a common pattern:
objecttype.datasourcename.propertyname
- The name of a given configuration property always begins with the
objecttype portion: a fully-qualified Java class name of the object
being configured. Configuration for Helidon MP’s named data source integration
concerns the behavior of
javax.sql.DataSourceobjects, so Helidon MP’s named data source integration configuration property names begin withjavax.sql.DataSource.- A period (
.) separates the objecttype portion from the rest of the property name.
- A period (
- The datasourcename portion, the name of the data source being
configured, comes next. It cannot contain a period (
.).- A period (
.) separates the datasourcename portion from the rest of the property name.
- A period (
- The propertyname portion, identifying the connection-pool- or
vendor-supplied-
DataSource-specific configuration property name, comes last. It may contain periods (.).
As an example, configuration to set an imaginary foo.bar property on the
test data source’s associated connection pool or vendor-specific DataSource
to baz looks like this in Java .properties format:
Examples
Here are some examples illustrating general named data source configuration patterns in various common MicroProfile Config-compatible locations.
Example: META-INF/microprofile-config.properties Classpath Resource
Here is an example of some named data source configuration as might be found in
a src/main/resources/META-INF/microprofile-config.properties configuration
source:
javax.sql.DataSource.yourDataSourceName.somePropertyOfYourConnectionPoolAndDataSource = itsValue
javax.sql.DataSource.yourDataSourceName.someOtherPropertyOfYourConnectionPoolAndDataSource = anotherValue
Example: System Properties Set on the Command Line
Here is an example of some named data source configuration using system properties on the command line instead:
java \
-Djavax.sql.DataSource.yourDataSourceName.somePropertyOfYourConnectionPoolAndDataSource=itsValue \
-Djavax.sql.DataSource.yourDataSourceName.someOtherPropertyOfYourConnectionPoolAndDataSource=anotherValue \
# ...
Example: Environment Variables Set on the Command Line
Here is an example of some named data source configuration using environment variables as typed directly into a command line shell, relying on MicroProfile Config’s mapping rules, since many shells will not understand environment variable names with periods (.) in them:
JAVAX_SQL_DATASOURCE_YOURDATASOURCENAME_SOMEPROPERTYOFYOURCONNECTIONPOOLANDDATASOURCE=itsValue \
JAVAX_SQL_DATASOURCE_YOURDATASOURCENAME_SOMEOTHERPROPERTYOFYOURCONNECTIONPOOLANDDATASOURCE=anotherValue \
java # ...
Example: Environment Variables Set By the env Command
Here is an example of some named data source configuration using environment
variables as supplied via the env shell command, thus
removing the need for MicroProfile Config’s mapping rules:
env 'javax.sql.DataSource.yourDataSourceName.somePropertyOfYourConnectionPoolAndDataSource=itsValue' \
'javax.sql.DataSource.yourDataSourceName.someOtherPropertyOfYourConnectionPoolAndDataSource=anotherValue' \
java # ...
Example: application.yaml Classpath Resource
Here is an example of some named data source configuration as might be found in
a src/main/resources/application.yaml classpath resource:
javax:
sql:
DataSource:
yourDataSourceName:
somePropertyOfYourConnectionPoolAndDataSource: itsValue
someOtherPropertyOfYourConnectionPoolAndDataSource: anotherValue
Example: Configuring the Oracle Universal Connection Pool and Oracle JDBC
This example presumes you have:
This example, in Java properties file format, configures an Oracle Universal
Connection Pool-managed data source named main to connect to an Oracle
Database on localhost port 1521, using the
oracle.jdbc.poolOracleDataSource vendor-supplied DataSource, with a service
name of XE, a user of scott, and a password of tiger:
In general, the properties that can be set on the Oracle Universal Connection
Pool can be inferred from the "setter" methods found in the Javadoc for the
PoolDataSourceImpl class.
In general, the properties that can be set on the
oracle.jdbc.pool.OracleDataSource DataSource
implementation can be inferred from the "setter" methods found in its
Javadoc.
DataSource implementation whose connections it pools.For example, in the example above it is not possible to tell that
connectionFactoryClassName is a property of the Oracle
Universal Connection Pool, and user is a property of the
oracle.jdbc.pool.OracleDataSource DataSource
implementation. In some cases, the Oracle Universal Connection Pool will set the
given property on both the connection pool itself and on
the vendor-supplied DataSource it manages.
Example: Configuring the HikariCP Connection Pool and H2
This example presumes you have:
This example, in Java properties file format, configures a HikariCP-managed data
source named test to connect to an in-memory H2 database named unit-testing
with a user of sa and an empty password:
HikariCP’s configuration properties are described on its GitHub
repository. Properties that should be forwarded on to the
vendor-supplied DataSource are prefixed with dataSource.
as seen in the example above.
In general, the properties that can be set on the
org.h2.jdbcx.JdbcDataSource vendor-supplied DataSource
can be inferred from the "setter" methods found in its
Javadoc.
Usage
You use Helidon MP’s named data source integration in the same way, regardless
of your choices of vendor-supplied DataSource and connection pool.
To use Helidon MP’s named data source integration in your application, once it
has been set up and configured, create an
ordinary DataSource-typed injection point in a Java class
representing a CDI bean somewhere in your application,
annotated with the name of the data source you wish to use.
Here is how to define such a field-backed injection point:
Jakarta Transactions (JTA) Integration
Overview
Helidon MP’s Jakarta Transactions integration integrates the Naryana
transaction engine, an implementation of the Jakarta
Transactions Specification, into Helidon MP. It lets you
use @jakarta.transaction.Transactional to declare JTA
transactions in your Java code.
Maven Coordinates (JTA)
To include Helidon’s JTA integration in your application:
- Ensure your dependencies are managed
- Ensure the following
<dependency>elements are present as child elements of your project’spom.xmlfile’s<dependencies>element:
Configuration
Overview
Helidon MP’s Jakarta Transactions integration does not require configuration, but configuration is possible. Because configuration is of the underlying Narayana transaction engine, any restrictions are those of the engine, not of Helidon itself.
Narayana, unlike Helidon MP, does not use MicroProfile Config, so its configuration options are less flexible.
Some common examples of Narayana configuration follow.
Configuring the Object Store Directory
Narayana features an object store directory which it uses to store information
about transaction outcomes. To set its location, you may set the
ObjectStoreEnvironmentBean.objectStoreDir system property
to the full path of a writeable directory:
java -DObjectStoreEnvironmentBean.objectStoreDir=/var/tmp # ...
See Specifying the object store location for more information.
Configuring the Default Transaction Manager Timeout
To configure Narayana’s default transaction manager timeout,
set the com.arjuna.ats.arjuna.coordinator.defaultTimeout system property to an
integral value in seconds:
java -Dcom.arjuna.ats.arjuna.coordinator.defaultTimeout=60 # ...
For more on configuring Narayana, see Setting Properties in the Naryana documentation.
Usage
To use Helidon MP’s Jakarta Transactions integration, annotate a method with the
jakarta.transaction.Transactional annotation:
Jakarta Persistence (JPA)
Overview
Helidon MP’s Jakarta Persistence integration allows you to
interact with Jakarta Persistence (JPA) objects as if your code were running in
an application server, handling automatic creation and management of objects
such as EntityManager and EntityManagerFactory instances.
More pragmatically, it allows you to inject managed
EntityManager instances using the
@PersistenceContext annotation.
Jakarta Persistence is a Jakarta EE specification that describes, among other things, how its implementations:
- Map Java objects to relational database tables
- Manage such persistent Java objects
- Interact with Jakarta Transactions
- Interact with named data sources
Jakarta Persistence may be used in an entirely application-managed manner, which requires no integration at all. This application-managed mode places the burden of error handling, thread safety, transaction management, and other concerns on the user. This documentation does not cover application-managed mode JPA.
Jakarta Persistence may also (preferably) be used in a fully container-managed manner, which requires that a container, like Helidon MP, handle error management, thread safety and transaction management on behalf of the user. This documentation covers this container-managed mode of JPA exclusively.
Helidon MP’s Jakarta Persistence integration comes with support for two JPA implementations, known as JPA providers:
In any given project, you use one or the other, but not both.
How you set up Helidon MP’s Jakarta Persistence integration differs depending on which of these JPA providers you choose to use.
Jakarta Persistence requires Jakarta Transactions and makes use of named data sources, so as you set up your project you will need to understand:
Project Setup
Setting Up a JPA Provider
Overview
While the Jakarta Persistence specification standardizes many aspects around programming and usage, it deliberately leaves many required setup and configuration aspects up to the JPA provider. You will need to set up your project differently depending on which JPA provider you choose.
To set up Helidon MP’s Jakarta Persistence integration in your application to work with your chosen JPA provider, you must:
- Set up and configure named data sources as appropriate
- Set up and configure Helidon MP’s Jakarta Transactions support
- Include the proper Jakarta Persistence-related dependencies
- Set up your project to generate and compile the static metamodel
- Set up your project for static weaving
Details and examples for each supported JPA provider are below.
Maven Coordinates (Common)
To include the Jakarta Persistence APIs that you will need and to include the core of Helidon’s Jakarta Persistence integration:
- Ensure your dependencies are managed
- Ensure you have set up and configured named data sources as appropriate
- Ensure you have set up and configured Helidon MP’s Jakarta Transactions support
- Ensure the following
<dependency>elements are present as child elements of your project’spom.xmlfile’s<dependencies>element:
These <dependency> elements do not set up a JPA provider. See details below
for the JPA provider you have chosen to use.
Setting Up Static Metamodel Generation
To generate and compile the Jakarta Persistence static metamodel for your
application, regardless of whether you are using Hibernate ORM or EclipseLink,
ensure your dependencies are managed, and then
make sure the <plugin> element in the following code snippet is present as a
child element of the <pluginManagement><plugins> element sequence as shown
below:
For more on the Hibernate ORM hibernate-jpamodelgen annotation processor, see
Hibernate Metamodel Generator in Hibernate ORM’s
documentation.
Maven Coordinates (Hibernate ORM)
To include Helidon’s Jakarta Persistence-related integration for Hibernate ORM:
- Ensure your dependencies are managed
- Ensure the basics of your JPA project are set up properly
- Ensure the following
<dependency>elements are present as child elements of your project’spom.xmlfile’s<dependencies>element:pom.xml<dependency> <groupId>io.helidon.integrations.cdi</groupId> <artifactId>helidon-integrations-cdi-hibernate</artifactId> <scope>runtime</scope> </dependency>- The
scopeisruntime, which ensures that Helidon MP’s Hibernate ORM integration is available at runtime.
- The
Setting Up Static Weaving (Hibernate ORM)
Hibernate ORM can alter your classes' bytecode at build time to keep track of changes made to objects participating in Jakarta Persistence workflows.
To set up this required static weaving for Hibernate ORM, ensure that the
following <plugin> element is present as a child element of your project’s
pom.xml file’s <plugins> element:
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<!--
Ideally, your plugin versions are managed via a
<pluginManagement> element, which is why the <version> element
is commented out below. If, nevertheless, you opt for the
explicit version, check
https://search.maven.org/artifact/org.hibernate.orm/hibernate-enhance-maven-plugin
for up-to-date versions, and make sure the version is the same
as that of Hibernate ORM itself.
-->
<!-- <version>6.3.1.Final</version> -->
<executions>
<execution>
<id>Statically enhance JPA entities for Hibernate</id>
<phase>compile</phase>
<goals>
<goal>enhance</goal>
</goals>
<configuration>
<failOnError>true</failOnError>
<enableDirtyTracking>true</enableDirtyTracking>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
</execution>
</executions>
</plugin>
For more on the hibernate-enhance-maven-plugin in particular, see its
documentation.
For more on Hibernate ORM’s bytecode enhancement (weaving) in general, see Bytecode Enhancement in Hibernate ORM’s documentation.
For more on bytecode enhancement properties, see Bytecode Enhancement Properties in Hibernate ORM’s documentation.
Maven Coordinates (EclipseLink)
To include Helidon’s Jakarta Persistence-related integration for EclipseLink:
- Ensure your dependencies are managed
- Ensure the basics of your JPA project are set up properly
- Ensure the following
<dependency>elements are present as child elements of your project’spom.xmlfile’s<dependencies>element:
Setting Up Static Weaving (EclipseLink)
EclipseLink can alter your classes' bytecode at build time to keep track of changes made to objects participating in Jakarta Persistence workflows.
To set up this required static weaving for EclipseLink, ensure that the
following <plugin> element is present as a child element of your project’s
pom.xml file’s <plugins> element:
For more on the EclipseLink static weaving command-line utility, see Static Weaving in the EclipseLink documentation.
Configuration
To configure Helidon MP’s Jakarta Persistence integration, you author a
META-INF/persistence.xml file. It contains a mix of
standardized elements and JPA provider-specific properties.
If you are writing a component or a library, then you place this in your Maven
project’s src/test/resources directory (because a library or component is not
itself an application, and by definition can be included in many applications,
so it is inappropriate to put application-level configuration in your
component). If you are working on a project that contains the main method (or
similar) that starts your application, then and only then do you place a
META-INF/persistence.xml in your persistence-oriented Maven project’s
src/main/resources directory.
For details about the structure and syntax of the META-INF/persistence.xml
file, see persistence.xml file in the Jakarta Persistence
specification.
Use Only One META-INF/persistence.xml Per Application
Like any configuration, a META-INF/persistence.xml file is normally an
application-level concern, not a component-level concern. In other words,
your Java application, made up of various components, or libraries, some of
which you may have written, and many of which you have not, should normally have
exactly one META-INF/persistence.xml on its classpath, describing the
persistence-related aspects of the application in its particular environment.
There are very few use cases where multiple META-INF/persistence.xml classpath
resources are called for.
A common mistake is to write a component or library—by definition intended for
use in possibly more than one application—and include a
src/main/resources/META-INF/persistence.xml in its Maven project. If two
components or libraries containing META-INF/persistence.xml classpath
resources like this are deployed as part of an application, it can make for a
confusing state of affairs at application runtime, and may lead to exceptions
indicating more persistence units are present than are expected.
Most library projects that work with JPA artifacts should probably have a
src/test/resources/META-INF/persistence.xml in their Maven projects instead.
This allows you to test your JPA-centric work against a test configuration,
rather than a "main" or production one, which is almost certainly what you want
in nearly all cases.
Persistence Units
Fundamentally, a META-INF/persistence.xml file contains a collection of
persistence units. A persistence unit represents a collection of entities in a
relational database loosely coupled to a named data
source that knows how to connect to it.
Your META-INF/persistence.xml file must begin (and end) with the following
XML:
Persistence Unit
You list your application’s persistence units as <persistence-unit> child
elements of the enclosing <persistence> element. Each <persistence-unit>
element identifies a named persistence unit that will correspond to an
EntityManager in your code, and represents a collection of entities in a
relational database.
Example: Persistence Unit Skeleton
Here is a partial example of a persistence unit named test with a helpful
description:
<persistence-unit> is represented in Jakarta Persistence as an instance of
the PersistenceUnitInfo class.JTA Data Source
A persistence unit is always associated with exactly one named data source.
Because Helidon MP’s Jakarta Persistence integration provides support for
container-managed JPA, and because container-managed JPA requires Jakarta
Transactions (JTA), the kind of named data source a persistence unit is
associated with is always a JTA data source. The
<jta-data-source> element, a child of the <persistence-unit> element, is how
you link a persistence unit to a named data
source you previously
configured.
Example: Persistence Unit with JTA Data Source
Here is a partial example of a persistence unit named test, with a helpful
description, linked with a JTA data source named main:
Classes
A persistence unit lists the classes that should be managed and that will take part in Jakarta Persistence workflows. You must list:
You use a sequence of <class> elements to do this. Each
<class> element contains the fully-qualified class name of one of the types of
managed classes listed above.
META-INF/persistence.xml
file to describe managed classes, but they may or may not
be honored by a given JPA provider.Example: Persistence Unit with Class Elements
Here is a partial example of a persistence unit named test, with a helpful
description, linked with a JTA data source named main, containing two entity
classes:
Properties
Persistence units can have simple properties attached to them to further
configure the backing JPA provider. You use the <properties>
element to specify them.
jakarta.persistence.jdbc.url, do not apply and will
be ignored if present. See the JTA Data Source section
above for how a persistence unit is linked to a named data
source.Example: Persistence Unit with Properties
Here is a partial exmaple of a persistence unit named test, with a helpful
description, linked with a JTA data source named sample, containing two entity
classes, configuring a Hibernate ORM-specific property:
Usage
To use Helidon MP’s Jakarta Persistence integration, once you have set up and configured your project, you use the Jakarta Persistence APIs in almost the same manner as if your project were deployed to a Jakarta EE application server.
Specifically, you:
- Annotate your managed classes (entities, mapped superclasses, etc.)
appropriately (using
@Entityand similar annotations) - Inject
EntityManagerinstances appropriately with the@PersistenceContextannotation - Use an injected
EntityManagerto work with your managed objects
In addition, you use Helidon MP’s JTA integration to declare transactional boundaries where appropriate.
A full tutorial of Jakarta Persistence is well beyond the scope of this documentation. Consult the specification for details on how to map your entity classes to relational database tables, and how to perform other related tasks.
Examples
Reference
- Managing Dependencies in Helidon MP
- MicroProfile Config in Helidon MP
- JDBC 4.3 Specification
- HikariCP 5.0.1 documentation
- Developers Guide For Oracle JDBC 21c on Maven Central
- Oracle® Universal Connection Pool Developer’s Guide, Release 21c
- Oracle® Universal Connection Pool Java API Reference, Release 21c
- Oracle® Database JDBC Developer’s Guide and Reference, Release 21c
- Oracle® Database JDBC Java API Reference, Release 21c
- H2 Database Engine documentation
- Jakarta Transactions 2.0 Specification
- Jakarta Transactions 2.0 API Reference
- Narayana Project Documentation
- Narayana API Reference
- Jakarta Persistence 3.1 Specification
- Jakarta Persistence 3.1 API Reference
- Hibernate ORM User Guide
- EclipseLink documentation