1. Overview

Most JPA-driven applications make heavy use of the “persistence.xml” file for getting a JPA implementation, such as Hibernate or OpenJPA.

Our approach here provides a centralized mechanism for configuring one or more persistence units and the associated persistence contexts.

And while this approach isn't inherently wrong, it's not suitable for use cases where it's necessary to test in isolation the application components that use different persistence units.

On the bright side, it's possible to bootstrap a JPA implementation without resorting to the “persistence.xml” file at all, by just using plain Java.

In this tutorial, we'll see how to accomplish this with Hibernate.

2. Implementing the PersistenceUnitInfo Interface

In a typical “xml-based” JPA configuration, the JPA implementation automatically takes care of implementing the PersistenceUnitInfo interface.

Using all the data gathered by parsing the “persistence.xml” file, the persistence provider uses this implementation to create an entity manager factory. From this factory, we can obtain an entity manager.

Since we won't rely on the “persistence.xml” file, the first thing that we need to do is to provide our own PersistenceUnitInfo implementation. We'll use Hibernate for our persistence provider:

public class HibernatePersistenceUnitInfo implements PersistenceUnitInfo {
    
    public static String JPA_VERSION = "2.1";
    private String persistenceUnitName;
    private PersistenceUnitTransactionType transactionType
      = PersistenceUnitTransactionType.RESOURCE_LOCAL;
    private List<String> managedClassNames;
    private List<String> mappingFileNames = new ArrayList<>();
    private Properties properties;
    private DataSource jtaDataSource;
    private DataSource nonjtaDataSource;
    private List<ClassTransformer> transformers = new ArrayList<>();
    
    public HibernatePersistenceUnitInfo(
      String persistenceUnitName, List<String> managedClassNames, Properties properties) {
        this.persistenceUnitName = persistenceUnitName;
        this.managedClassNames = managedClassNames;
        this.properties = properties;
    }

    // standard setters / getters   
}

In a nutshell, the HibernatePersistenceUnitInfo class is just a plain data container, which stores the configuration parameters bound to a specific persistence unit. This includes the persistence unit name, the managed entity classes' names, the transaction type, the JTA and non-JTA data sources, and so forth.

3. Creating an Entity Manager Factory With Hibernate's EntityManagerFactoryBuilderImpl Class

Now that we have a custom PersistenceUnitInfo implementation in place, the last thing that we need to do is get an entity manager factory.

Hibernate makes this process a breeze, with its EntityManagerFactoryBuilderImpl class (a neat implementation of the builder pattern).

To provide a higher level of abstraction, let's create a class that wraps the functionality of EntityManagerFactoryBuilderImpl.

First, let's showcase the methods that take care of creating an entity manager factory and an entity manager, using Hibernate's EntityManagerFactoryBuilderImpl class and our HibernatePersistenceUnitInfo class:

public class JpaEntityManagerFactory {
    private String DB_URL = "jdbc:mysql://databaseurl";
    private String DB_USER_NAME = "username";
    private String DB_PASSWORD = "password";
    private Class[] entityClasses;
    
    public JpaEntityManagerFactory(Class[] entityClasses) {
        this.entityClasses = entityClasses;
    }
    
    public EntityManager getEntityManager() {
        return getEntityManagerFactory().createEntityManager();
    }
    
    protected EntityManagerFactory getEntityManagerFactory() {
        PersistenceUnitInfo persistenceUnitInfo = getPersistenceUnitInfo(
          getClass().getSimpleName());
        Map<String, Object> configuration = new HashMap<>();
        return new EntityManagerFactoryBuilderImpl(
          new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration)
          .build();
    }
    
    protected HibernatePersistenceUnitInfo getPersistenceUnitInfo(String name) {
        return new HibernatePersistenceUnitInfo(name, getEntityClassNames(), getProperties());
    }

    // additional methods
}

Next, let's take a look at the *methods that provide the parameters required by EntityManagerFactoryBuilderImpl and HibernatePersistenceUnitInfo*.

These parameters include the managed entity classes, the entity classes' names, Hibernate's configuration properties, and a MysqlDataSource object:

public class JpaEntityManagerFactory {
    //...
    
    protected List<String> getEntityClassNames() {
        return Arrays.asList(getEntities())
          .stream()
          .map(Class::getName)
          .collect(Collectors.toList());
    }
    
    protected Properties getProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        properties.put("hibernate.id.new_generator_mappings", false);
        properties.put("hibernate.connection.datasource", getMysqlDataSource());
        return properties;
    }
    
    protected Class[] getEntities() {
        return entityClasses;
    }
    
    protected DataSource getMysqlDataSource() {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        mysqlDataSource.setURL(DB_URL);
        mysqlDataSource.setUser(DB_USER_NAME);
        mysqlDataSource.setPassword(DB_PASSWORD);
        return mysqlDataSource;
    }
}

For simplicity's sake, we've hard-coded the database connection parameters within the JpaEntityManagerFactory class. In production, though, we should store these in a separate properties file.

Furthermore, the getMysqlDataSource() method returns a fully-initialized MysqlDataSource object.

We've done this just to keep things easy to follow. In a more realistic, loosely-coupled design, we would **inject a DataSource object using EntityManagerFactoryBuilderImpl's withDataSource() method, rather than creating it within the class.

4. Performing CRUD Operations With an Entity Manager

Finally, let's see how to use a JpaEntityManagerFactory instance for getting a JPA entity manager and performing CRUD operations. (Note that we've omitted the User class for brevity's sake):

public static void main(String[] args) {
    EntityManager entityManager = getJpaEntityManager();
    User user = entityManager.find(User.class, 1);
    
    entityManager.getTransaction().begin();
    user.setName("John");
    user.setEmail("[email protected]");
    entityManager.merge(user);
    entityManager.getTransaction().commit();
    
    entityManager.getTransaction().begin();
    entityManager.persist(new User("Monica", "[email protected]"));
    entityManager.getTransaction().commit();
 
    // additional CRUD operations
}

private static class EntityManagerHolder {
    private static final EntityManager ENTITY_MANAGER = new JpaEntityManagerFactory(
      new Class[]{User.class})
      .getEntityManager();
}

public static EntityManager getJpaEntityManager() {
    return EntityManagerHolder.ENTITY_MANAGER;
}

5. Conclusion

In this article, we showed how to programmatically bootstrap a JPA entity manager using a custom implementation of JPA's PersistenceUnitInfo interface and Hibernate's EntityManagerFactoryBuilderImpl class, without having to rely on the traditional “persistence.xml” file.

As usual, all the code samples shown in this article are available over on GitHub.