Follow @vlad_mihalcea Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

One of my Twitter followers asked me if there is any documentation for bootstrapping JPA programmatically so that we can replace the persistence.xml configuration file with a Java configuration:

Hey @vlad_mihalcea is there a way in which persistence.xml file can be replaced with Java config class? Any documentation related to this would be really helpful. — Shyam Baitmangalkar (@SBaitmangalkar) November 28, 2017

Previously, I wrote an article about bootstrapping Hibernate without the persistence.xml , but that solution was based on the Hibernate-specific EntityManagerFactoryBuilderImpl .

In this article, I’m going to give you a solution that works with any JPA provider since it’s based on standard Java Persistence API.

PersistenceUnitInfo

The JPA specification PersistenceUnitInfo interface encapsulates everything is needed for bootstrapping an EntityManagerFactory . Typically, this interface is implemented by the JPA provider to store the info retrieved after parsing the persistence.xml configuration file.

Because we will no longer use a persistence.xml configuration file, we have to implement this interface ourselves. For the purpose of this test, consider the following implementation:

public class PersistenceUnitInfoImpl implements PersistenceUnitInfo { public static final String JPA_VERSION = "2.1"; private final String persistenceUnitName; private PersistenceUnitTransactionType transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL; private final List<String> managedClassNames; private final List<String> mappingFileNames = new ArrayList<>(); private final Properties properties; private DataSource jtaDataSource; private DataSource nonJtaDataSource; public PersistenceUnitInfoImpl( String persistenceUnitName, List<String> managedClassNames, Properties properties) { this.persistenceUnitName = persistenceUnitName; this.managedClassNames = managedClassNames; this.properties = properties; } @Override public String getPersistenceUnitName() { return persistenceUnitName; } @Override public String getPersistenceProviderClassName() { return HibernatePersistenceProvider.class.getName(); } @Override public PersistenceUnitTransactionType getTransactionType() { return transactionType; } @Override public DataSource getJtaDataSource() { return jtaDataSource; } public PersistenceUnitInfoImpl setJtaDataSource( DataSource jtaDataSource) { this.jtaDataSource = jtaDataSource; this.nonJtaDataSource = null; transactionType = PersistenceUnitTransactionType.JTA; return this; } @Override public DataSource getNonJtaDataSource() { return nonJtaDataSource; } public PersistenceUnitInfoImpl setNonJtaDataSource( DataSource nonJtaDataSource) { this.nonJtaDataSource = nonJtaDataSource; this.jtaDataSource = null; transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL; return this; } @Override public List<String> getMappingFileNames() { return mappingFileNames; } @Override public List<URL> getJarFileUrls() { return Collections.emptyList(); } @Override public URL getPersistenceUnitRootUrl() { return null; } @Override public List<String> getManagedClassNames() { return managedClassNames; } @Override public boolean excludeUnlistedClasses() { return false; } @Override public SharedCacheMode getSharedCacheMode() { return SharedCacheMode.UNSPECIFIED; } @Override public ValidationMode getValidationMode() { return ValidationMode.AUTO; } public Properties getProperties() { return properties; } @Override public String getPersistenceXMLSchemaVersion() { return JPA_VERSION; } @Override public ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } @Override public void addTransformer(ClassTransformer transformer) { } @Override public ClassLoader getNewTempClassLoader() { return null; } }

JUnit base class

To make it easier to reuse the bootstrapping logic, we can define an AbstractJPAProgrammaticBootstrapTest base class which will be extended by all our unit tests that want to bootstrap without an external persistence.xml configuration file.

The AbstractJPAProgrammaticBootstrapTest creates an EntityManagerFactory upon starting a new test and closes it after executing the unit test. This way, all tests run in isolation and each test class can be self-contained as well.

private EntityManagerFactory emf; public EntityManagerFactory entityManagerFactory() { return emf; } @Before public void init() { PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo( getClass().getSimpleName() ); Map<String, Object> configuration = new HashMap<>(); Integrator integrator = integrator(); if (integrator != null) { configuration.put( "hibernate.integrator_provider", (IntegratorProvider) () -> Collections.singletonList(integrator) ); } emf = new HibernatePersistenceProvider() .createContainerEntityManagerFactory( persistenceUnitInfo, configuration ); } @After public void destroy() { emf.close(); }

The JPA standard defines the PersistenceProvider interface defines the contract for instantiating a new EntityManagerFactory . We are going to use the HibernatePersistenceProvider which is a Hibernate-specific implementation of this interface. If you want to use a different JPA Provider, you have to check the Provider API for the PersistenceProvider implementation and use that instead.

Now, let’s see what the persistenceUnitInfo looks like:

protected PersistenceUnitInfoImpl persistenceUnitInfo(String name) { PersistenceUnitInfoImpl persistenceUnitInfo = new PersistenceUnitInfoImpl( name, entityClassNames(), properties() ); String[] resources = resources(); if (resources != null) { persistenceUnitInfo.getMappingFileNames().addAll( Arrays.asList(resources) ); } return persistenceUnitInfo; }

The entity classes and the associated XML configuration are defined by the following methods:

protected abstract Class<?>[] entities(); protected String[] resources() { return null; } protected List<String> entityClassNames() { return Arrays.asList(entities()) .stream() .map(Class::getName) .collect(Collectors.toList()); }

So, we can simply implement the entities or extend the resources method to provide the JPA mapping information programmatically.

The properties method defines some basic properties needed by all tests like auto-generating the schema or providing a JDBC DataSource to connect to the underlying database.

protected Properties properties() { Properties properties = new Properties(); properties.put( "hibernate.dialect", dataSourceProvider().hibernateDialect() ); properties.put( "hibernate.hbm2ddl.auto", "create-drop" ); DataSource dataSource = newDataSource(); if (dataSource != null) { properties.put( "hibernate.connection.datasource", dataSource ); } properties.put( "hibernate.generate_statistics", Boolean.TRUE.toString() ); return properties; }

Of course, we can extend the properties base class method to provide additional properties.

The newDataSource method is defined as follows:

protected DataSource newDataSource() { return proxyDataSource() ? dataSourceProxyType().dataSource( dataSourceProvider().dataSource()) : dataSourceProvider().dataSource(); } protected DataSourceProxyType dataSourceProxyType() { return DataSourceProxyType.DATA_SOURCE_PROXY; } protected boolean proxyDataSource() { return true; } protected DataSourceProvider dataSourceProvider() { return database().dataSourceProvider(); } protected Database database() { return Database.HSQLDB; }

The dataSourceProxyType allows us to proxy the underlying JDBC DataSource so that we can use the datasource-proxy Open-Source project to log SQL statements along with their bind parameter values.

However, you are not limited to standard JPA bootstrapping only since Hibernate allows you to integrate your own bootstrapping logic via the Integrator mechanism.

By default, we don’t provide any Integrator :

protected Integrator integrator() { return null; }

But if we provide an Integrator , this one is passed to Hibernate via the hibernate.integrator_provider configuration property.

For more details about how the Integrator mechanism works, check out this article about accessing the database table metadata using Hibernate.

And that’s pretty much it!

Specific JUnit test

Now that we have the AbstractJPAProgrammaticBootstrapTest in place, a specific test will look as follows:

public class BootstrapTest extends AbstractJPAProgrammaticBootstrapTest { @Override protected Class<?>[] entities() { return new Class[] { Post.class }; } @Test public void test() { doInJPA(entityManager -> { for (long id = 1; id <= 3; id++) { Post post = new Post(); post.setId(id); post.setTitle( String.format( "High-Performance Java Persistence, Part %d", id ) ); entityManager.persist(post); } }); } @Entity(name = "Post") @Table(name = "post") public static class Post { @Id private Long id; private String title; //Getters and setters omitted for brevity } }

We just have to extend the AbstractJPAProgrammaticBootstrapTest base class and define the entities we want to use. Notice that the entities are associated with this test only so that we make sure that entity mapping changes don’t ripple to other tests.

The doInJPA is a utility method that you can see in this article I wrote before.

Conclusion

As you can see, bootstrapping JPA without the persistence.xml is very easy since the Java Persistence standard defines the contract to do so.

Insert details about how the information is going to be processed DOWNLOAD NOW