|

A Beginner’s Guide to JPA’s persistence.xml


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


In JPA, the persistence.xml file is the central piece of configuration. That makes it one of the most important files of your persistence layer. So, it shouldn’t be a surprise that the JPA for Beginners in the Persistence Hub contains an entire lecture about it. And I thought it’s also something that I should share here on the blog.

The persistence.xml file defines one or more persistence units, and you can configure things like:

  • the name of each persistence unit,
  • which managed persistence classes are part of a persistence unit,
  • how these classes shall be mapped to database tables,
  • the persistence provider that shall be used at runtime,
  • the data source you want to use to connect to your database,
  • how to create and validate the database schema,
  • the mode of your 2nd level cache,
  • several provider-specific configuration parameters.

As you can see, you can configure many things in this file. But don’t worry. It isn’t as complex or overwhelming as you might expect.

Differences introduced in JPA 3.0

But before we take a look at the first configuration, I need to address the changes introduced in JPA 3.0. As part of the transformation from Java EE to Jakarta EE, the Java Persistence API (JPA) was renamed to Jakarta Persistence API (JPA). Unfortunately, this also affected the code and configuration files. The prefix “javax.persistence.” had to be replaced with “jakarta.persistence.” in all package names, configuration property names and XML namespaces.

I will mention the different property names in the following sections. However, I will not include the XML namespace declarations to make the configuration examples easier to read.

If you’re using JPA 2.x (EclipseLink <3.0, Hibernate <5.5.0), you should use the following namespaces.

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" xsi:schemalocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="my-persistence-unit">
        ...
    </persistence-unit>
</persistence>

And if you’re using JPA 3.x, you should use the updated namespaces.

<persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0" xsi:schemalocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
    <persistence-unit name="my-persistence-unit">
        ...
    </persistence-unit>
</persistence>

The most basic persistence.xml configuration

You can use JPA with a very short, basic configuration. You only need a persistence element as the root element and a persistence-unit element with a name attribute. The attribute is used to identify the persistence unit, and you can use it during the bootstrapping process to instantiate a specific EntityManagerFactory.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
    </persistence-unit>
</persistence>

When you use this configuration, you configure a persistence unit with the name “my-persistence-unit” without defining a dependency to a specific JPA implementation. You also rely on a list of defaults defined by the specification:

  • Your persistence provider scans the root of your persistence unit and adds all annotated managed persistence classes to the persistence unit.
  • If your META-INF directory contains a file called orm.xml, it gets treated as a mapping file and all included mapping information get used.
  • The used transaction type depends on the environment in which you deploy your application. In a Jakarta EE environment, JPA expects that the container provides a JTA-compliant connection provider. In a Java SE environment, it uses a RESOURCE_LOCAL transaction instead.
  • You don’t configure any database connection. JPA, therefore, expects that you provide a datasource at runtime.
  • All JPA implementations support a set of proprietary configuration parameters. Examples for that are the logging configuration in EclipseLink JPA or Hibernate’s database dialect. As you don’t define any of them in this basic configuration, you also rely on all provider-specific defaults.

Optional configuration elements you should know

Having a basic configuration makes it easy to get started. But in most of your projects, you need more than that. You might need to reference a specific datasource, include managed classes from a different jar file, activate the 2nd level cache or include an external mapping configuration.

So, let’s take a look at JPA’s optional configuration elements and what they enable you to do.

Provide a description of your persistence unit

Sometimes, a meaningful name is not enough. Especially in bigger, more complex deployments that include multiple persistence-units, it’s a good practice to provide a short description that tells more about the usage and purpose of each persistence-unit. You can do that with the description element.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <description>This is a short text describing my persistence unit.</description>
    </persistence-unit>
</persistence>

Specify the managed classes included in your persistence unit

By default, your JPA persistence-unit includes all annotated managed classes found in its root. If you want to include any classes that are located somewhere else, you can either reference them explicitly or include all classes from a jar file.

Include one or more specific managed classes

By adding one or more class elements to your persistence-unit configuration, you can add classes to your persistence unit which are not in the root of the persistence unit. Each class element needs to contain the fully referenced name of a class. Please be aware that these classes need to be available on your classpath.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <class>org.thoughts.on.java.jpa.beginners.Professor</class>
    </persistence-unit>
</persistence>

Include managed classes from other jar files

Using multiple class elements to add a long list of managed classes to your persistence unit can be a cumbersome task. It’s oftentimes easier to use one or more jar-file elements to add all managed classes contained in these jar files.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <jar-file>my-entities.jar</jar-file>
    </persistence-unit>
</persistence>

Exclude unlisted classes

You can not only add classes to your persistence-unit which are not at its root, you can also exclude classes which would be added by default. To do that, you first need to use one or more class elements to explicitly specify which managed classes shall be part of the persistence-unit. In the next step, you can use the exclude-unlisted-classes element to exclude all classes from the persistence-unit which were not explicitly included.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <class>org.thoughts.on.java.jpa.beginners.Professor</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
    </persistence-unit>
</persistence>

Reference a mapping file

Annotations are the most common but not your only option to define the mapping between your entities and database tables. You can also use external, XML-based mapping files. By default, your persistence provider checks if the META-INF directory contains a file called orm.xml and includes its mapping information. As I explained in a previous post, the mapping definitions provided via annotations and in the mapping files are merged during the deployment, and the information in the mapping file prevail.

If you want to use multiple mapping files or if the name of your file doesn’t match the default naming pattern, you can use one or more mapping-file elements to reference the files that shall be used with your persistence-unit.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <mapping-file>file:\\\C:\dev\wrk\XmlMapping\XmlMappings\myMappings.xml</mapping-file>
    </persistence-unit>
</persistence>

Use a specific persistence provider

If you use any proprietary features of your persistence provider, you should specify a dependency to it. You can do that by defining the name of the class that implements the jakarta.persistence.spi.PersistenceProvider interface (or the javax.persistence.spi.PersistenceProvider interface if you’re using JPA 2.x) in the provider element.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    </persistence-unit>
</persistence>

Reference a datasource

The jta-data-source and non-jta-data-source elements are mostly used in Jakarta EE environments. They enable you to reference the JNDI name of a datasource that is or is not compliant with the Java Transaction API. If you don’t use any of these two elements, you need to either configure a default JTA datasource for your container or provide the JNDI name of a datasource at deploy time.

And if you’re using JPA in a Java SE environment, you can either use one of these two elements to reference a datasource or use a set of properties to configure your database connection.

I use the jta-data-source parameter in the following example to reference a JTA datasource.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <jta-data-source>java:app/jdbc/MyDataSource</jta-data-source>
    </persistence-unit>
</persistence>

Activate the 2nd level cache

The 2nd level cache, which is defined by the JPA specification, is one of the bigger topics in my Hibernate Performance Tuning Online Training. You should use it to cache entities that you often read but only rarely change. It is deactivated by default. You can activate the cache and specify its mode with the shared-cache-mode element.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
    </persistence-unit>
</persistence>

You can choose between 4 different options:

  1. ALL – To cache all entities
  2. NONE – To cache none of your entities (default)
  3. ENABLE_SELECTIVE – To cache only the entities annotated with @Cacheable or @Cacheable(true)
  4. DISABLE_SELECTIVE – To cache all entities not annotated with @Cacheable(false)

I always recommend using ENABLE_SELECTIVE because it requires you to annotate an entity before it gets cached. So, you shouldn’t slow down your application by accidentally caching an entity that shouldn’t be cached.

Deactivate automatic validation of your entities

As I explained in more details in my article How to automatically validate entities with Hibernate Validator, JPA integrates with the bean validation specification. This enables you to automatically validate the values of your entity attributes before they get persisted or updated. The 3 different values supported by the validation-mode element enable you to activate or deactivate the validation:

  1. AUTO – Perform the validation if a bean validation implementation is available (default)
  2. CALLBACK– Activate the validation and throw an exception if no bean validation implementation is available
  3. NONE – Do not perform any validation
<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <validation-mode>CALLBACK</validation-mode>
    </persistence-unit>
</persistence>

Define additional properties

In addition to the previously described XML elements, you can use the properties element to configure additional standard and vendor-specific parameters and hints.

Configure timeouts

You can use the properties jakarta.persistence.lock.timeout and jakarta.persistence.query.timeout (or javax.persistence.lock.timeout and javax.persistence.query.timeout in JPA 2.x) to define the pessimistic lock timeout and the query timeout in milliseconds. These are just hints and your persistence provider might or might not use them. That depends on the persistence provider you use in your project and the database to which it connects.

<persistence>
    <persistence-unit name="my-persistence-unit">
        <properties>
            <!-- JPA 3.x -->
            <property name="jakarta.persistence.lock.timeout" value="100"/>
            <property name="jakarta.persistence.query.timeout" value="100"/>
 
            <!-- JPA 2.x -->
            <!-- property name="javax.persistence.lock.timeout" value="100"/>
            <property name="javax.persistence.query.timeout" value="100"/ -->
        </properties>
    </persistence-unit>
</persistence>

Specify Validation Groups

If you activate the automatic validation or rely on the default behavior, you can define custom groups which get validated before the persistence provider executes the persist, update or remove operations. You can configure one or more groups for each lifecycle state change by using the properties:

  • JPA 3.x
    • jakarta.persistence.validation.group.pre-persist
    • jakarta.persistence.validation.group.pre-update and 
    • jakarta.persistence.validation.group.pre-remove
  • JPA 2.x
    • javax.persistence.validation.group.pre-persist
    • javax.persistence.validation.group.pre-update and 
    • javax.persistence.validation.group.pre-remove .
<persistence>
    <persistence-unit name="my-persistence-unit">
        <properties>
            <!-- JPA 3.x -->
            <property name="jakarta.persistence.validation.group.pre-persist" value="jakarta.validation.groups.MyPersistValidation"/>
            <property name="jakarta.persistence.validation.group.pre-update" value="jakarta.validation.groups.MyUpdateValidation"/>
            <property name="jakarta.persistence.validation.group.pre-remove" value="jakarta.validation.groups.MyRemovetValidation"/>
 
            <!-- JPA 2.x -->
            <!-- property name="javax.persistence.validation.group.pre-persist" value="javax.validation.groups.MyPersistValidation"/>
            <property name="javax.persistence.validation.group.pre-update" value="javax.validation.groups.MyUpdateValidation"/>
            <property name="javax.persistence.validation.group.pre-remove" value="javax.validation.groups.MyRemovetValidation"/ -->
        </properties>
    </persistence-unit>
</persistence>

Configure a database connection in Java SE

In a Java SE environment, you might not have a datasource that you can reference to define the database connection. In these situations, you can use the following set of properties to specify the JDBC driver class, the connection URL and the login information that your persistence provider shall use to connect to the database:

  • jakarta.persistence.jdbc.driver / javax.persistence.jdbc.driver
    The fully qualified class name of your JDBC driver
  • jakarta.persistence.jdbc.url / javax.persistence.jdbc.url
    The connection URL of your database
  • jakarta.persistence.jdbc.user / javax.persistence.jdbc.user
    The user name to login to your database
  • jakarta.persistence.jdbc.password / javax.persistence.jdbc.password
    The password to login to your database
<persistence>
    <persistence-unit name="my-persistence-unit">
        <properties>
            <!-- JPA 3.x -->
            <property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/jpaForBeginners" />
            <property name="jakarta.persistence.jdbc.user" value="postgres" />
            <property name="jakarta.persistence.jdbc.password" value="postgres" />
 
            <!-- JPA 2.x -->
            <!-- property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/jpaForBeginners" />
            <property name="javax.persistence.jdbc.user" value="postgres" />
            <property name="javax.persistence.jdbc.password" value="postgres" / -->
        </properties>
    </persistence-unit>
</persistence>

Create and initialize the database

Since version 2.1, JPA can create a new database at startup and initialize it with a predefined dataset. But before you use this feature in your application, please be aware that it is not as powerful and flexible as a version-based database migration that I showed you in my tutorials about Flyway and Liquibase.

You can activate and configure this feature by adding the following properties to your configuration:

  • jakarta.persistence.schema-generation.database.action / javax.persistence.schema-generation.database.action
    The action that you want to performe to manage the database schema. Supported values are none (default), create, drop-and-create and drop. As you probably expect, the create option creates the database schema at startup, drop-and-create drops the existing database and creates a new one and drop just removes the existing database.
  • jakarta.persistence.schema-generation.create-script-source / javax.persistence.schema-generation.create-script-source
    The name or file URL to the script that creates the database.
  • jakarta.persistence.schema-generation.drop-script-source / javax.persistence.schema-generation.drop-script-source
    The name or file URL to the script that drops the database.
  • jakarta.persistence.sql-load-script-source / javax.persistence.sql-load-script-source
    The name or file URL to the script that initializes the database with a predefined dataset.
<persistence>
    <persistence-unit name="my-persistence-unit">
        <properties>
            <!-- DON'T USE THIS IN PRODUCTION -->
            <!-- automatically drop and create required database tables -->
 
            <!-- JPA 3.x -->
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create" />
            <property name="jakarta.persistence.schema-generation.create-script-source" value="create-db.sql" />
            <property name="jakarta.persistence.schema-generation.drop-script-source" value="drop-db.sql" />
            <property name="jakarta.persistence.sql-load-script-source" value="data.sql" />
 
            <!-- JPA 2.x -->
            <!-- property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
            <property name="javax.persistence.schema-generation.create-script-source" value="create-db.sql" />
            <property name="javax.persistence.schema-generation.drop-script-source" value="drop-db.sql" />
            <property name="javax.persistence.sql-load-script-source" value="data.sql" / -->
        </properties>
    </persistence-unit>
</persistence>

Generate your database scripts

And if you don’t have any SQL scripts to create or drop your database, you can create them based on your entity mappings. But please be aware that these scripts often need to be adapted and optimized before you can use them in production. But they are a good starting point and optimizing them is often times a lot faster than writing everything yourself.

You can tell your persistence provider to generate these scripts by configuring the following properties:

  • jakarta.persistence.schema-generation.scripts.action / javax.persistence.schema-generation.scripts.action
    The kind of scripts you want to generate. Supported values are none (default), create, drop-and-create and drop.
  • jakarta.persistence.schema-generation.create-source / javax.persistence.schema-generation.create-source
    The basis on which you want to create the database. You can choose between metadata, script, metadata-then-script and script-then-metadata.
  • jakarta.persistence.schema-generation.drop-source / javax.persistence.schema-generation.drop-source
    The basis on which you want to drop the database. You can choose between metadata, script, metadata-then-script and script-then-metadata.
  • jakarta.persistence.schema-generation.scripts.create-target / javax.persistence.schema-generation.scripts.create-target
    The location where you want to store the generated create script.
  • jakarta.persistence.schema-generation.scripts.drop-target / javax.persistence.schema-generation.scripts.drop-target
    The location where you want to store the generated drop script.
<persistence>
    <persistence-unit name="my-persistence-unit">
        <properties>
            <!-- JPA 3.x -->
            <property name="jakarta.persistence.schema-generation.scripts.action" value="drop-and-create"/>
            <property name="jakarta.persistence.schema-generation.scripts.create-target" value="./create.sql"/>
            <property name="jakarta.persistence.schema-generation.scripts.drop-target" value="./drop.sql"/>
 
            <!-- JPA 2.x -->
            <!-- property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>
            <property name="javax.persistence.schema-generation.scripts.create-target" value="./create.sql"/>
            <property name="javax.persistence.schema-generation.scripts.drop-target" value="./drop.sql"/ -->
        </properties>
    </persistence-unit>
</persistence>

A few standard persistence.xml configurations

OK, you now know the different elements and properties that you can use in the persistence.xml file. Let’s combine them to a few standard configurations that are a good fit for projects using Hibernate or EclipseLink.

I will use the package and property names defined by the Jakarta Persistence API specification. If you want to use the older Java Persistence API, please make sure to replace all occurrencesof “jakarta.persistence” with “javax.persistence”.

Configurations for Hibernate

Include all managed classes from external jar file, reference a datasource, and specify the dialect

The following configuration for Hibernate adds all managed persistence classes from the root and the my-entities.jar file to the persistence unit. It uses the JTA datasource java:app/jdbc/MyDataSource and tells Hibernate to use the database-specific dialect for PostgreSQL.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <description>JpaForBeginners</description>
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jar-file>my-entities.jar</jar-file>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
 
        <jta-data-source>java:app/jdbc/MyDataSource</jta-data-source>
 
        <properties>      
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

Include all managed classes, define a database connection, and specify the dialect

This configuration for Hibernate adds all managed persistence classes from the root to the persistence unit and uses a set of properties to connect to a PostgreSQL database on localhost. It also tells Hibernate to use the database-specific dialect for PostgreSQL.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <description>JpaForBeginners</description>
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
 
        <properties>
            <!-- database connection -->
            <property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/jpaForBeginners" />
            <property name="jakarta.persistence.jdbc.user" value="postgres" />
            <property name="jakarta.persistence.jdbc.password" value="postgres" />
                 
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

Configurations for EclipseLink

Include all managed classes from external jar file, reference a datasource and activate logging

The following configuration for EclipseLink adds all managed persistence classes from the root and the my-entities.jar file to the persistence unit. It uses the JTA datasource java:app/jdbc/MyDataSource and logs all executed SQL statements with the bind parameter values.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <description>JpaForBeginners</description>
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <jar-file>my-entities.jar</jar-file>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
 
        <jta-data-source>java:app/jdbc/MyDataSource</jta-data-source>
 
        <properties>      
            <!-- configure logging -->
            <property name="eclipselink.logging.level" value="INFO"/>
            <property name="eclipselink.logging.level.sql" value="FINE"/>
            <property name="eclipselink.logging.parameters" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Include all managed classes, define a database connection and activate logging

This configuration for EclipseLink adds all managed persistence classes from the root to the persistence unit and uses a set of properties to connect to a PostgreSQL database on localhost. It also logs all executed SQL statements with the bind parameter values.

<persistence>
    <!-- Define persistence unit -->
    <persistence-unit name="my-persistence-unit">
        <description>JpaForBeginners</description>
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
 
        <properties>
            <!-- database connection -->
            <property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/jpaForBeginners" />
            <property name="jakarta.persistence.jdbc.user" value="postgres" />
            <property name="jakarta.persistence.jdbc.password" value="postgres" />
                 
            <!-- configure logging -->
            <property name="eclipselink.logging.level" value="INFO"/>
            <property name="eclipselink.logging.level.sql" value="FINE"/>
            <property name="eclipselink.logging.parameters" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Summary

As you have seen in this article, JPA’S persistence.xml enables you to configure the most important parts of your persistence layer. You can define which managed persistence classes you want to include, to which database your persistence provider shall connect, if and how the 2nd level cache shall be used, and much more.

4 Comments

  1. Thanks for every other magnificent post. Where else could anyone get that
    type of info in such an ideal method of writing? I’ve a
    presentation next week, and I am at the search for such info.

  2. Is it possible to refer to environment variables inside properties? say I want do have javax.persistence.jdbc.url as env var. thanks

    1. Avatar photo Thorben Janssen says:

      No, not with plain JPA.
      If you’re using Spring or deploy your application in a Jakarta EE application server, you might have other frameworks in place which can handle that.

  3. very useful an clear. Thank you

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.