OpenJPA Jakarta tips
OpenJPA is a well known JPA implementation. Did you know it supports Jakarta namespace? Let see how to use it.
JPA is a JavaEE/JakartaEE specification but it is likely the least portable one so changing of provider is rarely an option + it is often part of the EE stack you use so it is always saner to use the one you have than customizing an EE server. If your provider is OpenJPA, let see how Jakarta namespace can be supported.
Dependencies
OpenJPA provides dependencies with jakarta
classifier to support Jakarta namespace.
To import them you need to import OpenJPA but also the JPA 3 dependency (specification API), SERP (transitive dependency for OpenJPA) and JTA API:
<!-- specs -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<version>2.0.0</version>
</dependency>
<!-- openjpa -->
<dependency>
<groupId>net.sourceforge.serp</groupId>
<artifactId>serp</artifactId>
<version>1.15.1</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>${openjpa.version}</version>
<classifier>jakarta</classifier>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
if you don’t use build time enhancement you must also import ASM (XBean ones) dependencies. |
Define your persistence unit without XML
Indeed you can use a persistence.xml
but OpenJPA is one of the rare provider enabling you to not have XML files.
If you are interested by this option, a common way to do it is to follow these steps:
-
Define a
PersistenceUnitInfo
implementation// mainly just a DTO public class PersistenceUnitInfoImpl implements PersistenceUnitInfo { private String persistenceXMLSchemaVersion = "2.0"; private String persistenceUnitName; private String persistenceProviderClassName; private DataSource jtaDataSource; private DataSource nonJtaDataSource; private List<String> managedClassNames; private URL persistenceUnitRootUrl; private PersistenceUnitTransactionType transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL; private List<String> mappingFileNames = List.of(); private List<URL> jarFileUrls = List.of(); private boolean excludeUnlistedClasses = true; private SharedCacheMode sharedCacheMode = SharedCacheMode.NONE; private ValidationMode validationMode = ValidationMode.NONE; private Properties properties = new Properties(); private ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private ClassLoader newTempClassLoader = classLoader; private final Collection<ClassTransformer> transformers = new ArrayList<>(); public PersistenceUnitInfoImpl persistenceXMLSchemaVersion(final String persistenceXMLSchemaVersion) { this.persistenceXMLSchemaVersion = persistenceXMLSchemaVersion; return this; } public PersistenceUnitInfoImpl persistenceUnitName(final String persistenceUnitName) { this.persistenceUnitName = persistenceUnitName; return this; } public PersistenceUnitInfoImpl persistenceProviderClassName(final String persistenceProviderClassName) { this.persistenceProviderClassName = persistenceProviderClassName; return this; } public PersistenceUnitInfoImpl jtaDataSource(final DataSource jtaDataSource) { this.jtaDataSource = jtaDataSource; return this; } public PersistenceUnitInfoImpl nonJtaDataSource(final DataSource nonJtaDataSource) { this.nonJtaDataSource = nonJtaDataSource; return this; } public PersistenceUnitInfoImpl managedClassNames(final List<String> managedClassNames) { this.managedClassNames = managedClassNames; return this; } public PersistenceUnitInfoImpl managedClasses(final List<Class<?>> managedClasses) { return managedClassNames(managedClasses.stream() .flatMap(this::hierarchy) .distinct() .map(Class::getName) .collect(toList())); } private Stream<Class<?>> hierarchy(final Class<?> it) { return it == null || it == Object.class ? Stream.empty() : Stream.concat(Stream.of(it), hierarchy(it.getSuperclass())); } public PersistenceUnitInfoImpl persistenceUnitRootUrl(final URL persistenceUnitRootUrl) { this.persistenceUnitRootUrl = persistenceUnitRootUrl; return this; } public PersistenceUnitInfoImpl transactionType(final PersistenceUnitTransactionType transactionType) { this.transactionType = transactionType; return this; } public PersistenceUnitInfoImpl mappingFileNames(final List<String> mappingFileNames) { this.mappingFileNames = mappingFileNames; return this; } public PersistenceUnitInfoImpl jarFileUrls(final List<URL> jarFileUrls) { this.jarFileUrls = jarFileUrls; return this; } public PersistenceUnitInfoImpl excludeUnlistedClasses(final boolean excludeUnlistedClasses) { this.excludeUnlistedClasses = excludeUnlistedClasses; return this; } public PersistenceUnitInfoImpl sharedCacheMode(final SharedCacheMode sharedCacheMode) { this.sharedCacheMode = sharedCacheMode; return this; } public PersistenceUnitInfoImpl validationMode(final ValidationMode validationMode) { this.validationMode = validationMode; return this; } public PersistenceUnitInfoImpl properties(final Properties properties) { this.properties = properties; return this; } public PersistenceUnitInfoImpl classLoader(final ClassLoader classLoader) { this.classLoader = classLoader; return this; } public PersistenceUnitInfoImpl newTempClassLoader(final ClassLoader newTempClassLoader) { this.newTempClassLoader = newTempClassLoader; return this; } @Override public String getPersistenceXMLSchemaVersion() { return persistenceXMLSchemaVersion; } @Override public String getPersistenceUnitName() { return persistenceUnitName; } @Override public String getPersistenceProviderClassName() { return persistenceProviderClassName; } @Override public PersistenceUnitTransactionType getTransactionType() { return transactionType; } @Override public DataSource getJtaDataSource() { return jtaDataSource; } @Override public DataSource getNonJtaDataSource() { return nonJtaDataSource; } @Override public List<String> getManagedClassNames() { return managedClassNames; } @Override public List<String> getMappingFileNames() { return mappingFileNames; } @Override public List<URL> getJarFileUrls() { return jarFileUrls; } @Override public URL getPersistenceUnitRootUrl() { return persistenceUnitRootUrl; } @Override public SharedCacheMode getSharedCacheMode() { return sharedCacheMode; } @Override public ValidationMode getValidationMode() { return validationMode; } @Override public Properties getProperties() { return properties; } @Override public ClassLoader getClassLoader() { return classLoader; } @Override public ClassLoader getNewTempClassLoader() { return newTempClassLoader; } @Override public boolean excludeUnlistedClasses() { return excludeUnlistedClasses; } @Override public void addTransformer(final ClassTransformer transformer) { transformers.add(transformer); } }
-
Load the
PersistenceProvider
:// if you can get multiple impl handle conflicts final var provider = ServiceLoader.load(PersistenceProvider.class).iterator().next();
-
Create the
EntityManagerFactory
:// config/depends your app final var ds = getDataSource(); final var props = getUnitProperties(); final var classes = getEntityNames(); final var name = getUnitName(); // create the emf final var emf = provider.createContainerEntityManagerFactory( new PersistenceUnitInfoImpl() .persistenceUnitName(name) .properties(props) .jtaDataSource(ds) .managedClasses(classes), Map.of())
-
Integrate the
EntityManagerFactory
in your application (spring-boot, Apache DeltaSpike, CDI, …).
Indeed, this option is mainly for standalone applications since EE containers will have it integrated out of the box but it enables to simplify the JPA setup a bit and unifying its configuration with your own application configuration which is always a plus when it enables to reduce the needed properties/entries.
Enhancement
JPA requires enhancement to be fully functional - even with Hibernates ;).
The most common way to do it for OpenJPA is to use its Maven Plugin.
It is the standard plugin, no jakarta
flavor needed there.
The only trick is to add to the plugin dependencies, the jakarta
openjpa jar.
<plugin>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-maven-plugin</artifactId>
<version>${openjpa.version}</version>
<executions>
<execution>
<id>enhancer</id>
<phase>compile</phase>
<goals>
<goal>enhance</goal>
</goals>
<configuration>
<toolProperties>
<MetaDataFactory>jpa</MetaDataFactory>
</toolProperties>
</configuration>
</execution>
</executions>
<configuration>
<includes>com/superbiz/jpa/**/*.class</includes>
<sqlAction>build</sqlAction>
<toolProperties>
<MappingDefaults>jpa(ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict)</MappingDefaults>
<MetaDataFactory>jpa</MetaDataFactory>
<Log>slf4j</Log>
</toolProperties>
</configuration>
<dependencies>
<!-- jakarta openjpa jar as in first section -->
</dependencies>
</plugin>
as you can see in this snippet, you can customize the logging of the plugin. Using <Log>slf4j</Log> enables to use Maven logging automatically which makes the output nicer.
|
Conclusion
Using jakarta
OpenJPA dependency and wiring it in its Maven plugin enables to develop and run OpenJPA entities under the jakarta
namespace.
Using a persistence.xml
or not is up to you but also enables some more advanced features which can open more doors to your configuration (exposed to end users).
From the same author:
In the same category: