Bootstrapping JPA Programmatically in Java – 在Java中以编程方式引导JPA

最后修改: 2018年 6月 16日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

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

大多数JPA驱动的应用程序大量使用“persistence.xml”文件来获取JPA实现,例如HibernateOpenJPA

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.

从好的方面来看,只要使用普通的Java,就有可能在不使用“persistence.xml”文件的情况下引导JPA的实现

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

在本教程中,我们将看到如何用Hibernate完成这个任务。

2. Implementing the PersistenceUnitInfo Interface

2.实现PersistenceUnitInfo 接口

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

在典型的 “基于xml “的JPA配置中,JPA实现会自动负责实现PersistenceUnitInfo接口。

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.

使用通过解析“persistence.xml”文件收集的所有数据,持久化提供者使用该实现来创建一个实体管理器工厂。从这个工厂,我们可以获得一个实体管理器。

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:

由于我们不会依赖“persistence.xml” 文件,我们需要做的第一件事是提供我们自己的PersistenceUnitInfo实现。我们将使用Hibernate作为我们的持久化提供者。

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.

简而言之,HibernatePersistenceUnitInfo类只是一个普通的数据容器,它存储了绑定到特定持久化单元的配置参数。这包括持久化单元名称、管理实体类的名称、事务类型、JTA和非JTA数据源等等。

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

3.用Hibernate的EntityManagerFactoryBuilderImpl类创建一个实体管理器工厂

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

现在我们已经有了一个自定义的PersistenceUnitInfo实现,我们需要做的最后一件事就是获得一个实体管理器工厂。

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

Hibernate通过其 EntityManagerFactoryBuilderImpl类(构建器模式的一个整洁的实现)使这个过程变得轻而易举。

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

为了提供更高层次的抽象,让我们创建一个类来包装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:

首先,让我们展示一下负责创建实体管理器工厂和实体管理器的方法,使用Hibernate的EntityManagerFactoryBuilderImpl类和我们的HibernatePersistenceUnitInfo类。

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.

接下来,让我们看看提供EntityManagerFactoryBuilderImplHibernatePersistenceUnitInfo所需参数的方法。

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

这些参数包括被管理的实体类、实体类的名称、Hibernate的配置属性,以及一个MysqlDataSource对象。

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.

为简单起见,我们在JpaEntityManagerFactory类中硬编码了数据库连接参数。但在生产中,我们应该将这些参数存储在一个单独的属性文件中。

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

此外,getMysqlDataSource()方法返回一个完全初始化的MysqlDataSource对象。

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.

我们这样做只是为了让事情更容易理解。在一个更现实的、松散耦合的设计中,我们会使用EntityManagerFactoryBuilderImplwithDataSource()方法注入一个数据源对象,而不是在类内创建它

4. Performing CRUD Operations With an Entity Manager

4.用实体管理器执行CRUD操作

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):

最后,让我们看看如何使用一个JpaEntityManagerFactory实例来获取JPA实体管理器并执行CRUD操作。(注意,为了简洁起见,我们省略了User类)。

public static void main(String[] args) {
    EntityManager entityManager = getJpaEntityManager();
    User user = entityManager.find(User.class, 1);
    
    entityManager.getTransaction().begin();
    user.setName("John");
    user.setEmail("john@domain.com");
    entityManager.merge(user);
    entityManager.getTransaction().commit();
    
    entityManager.getTransaction().begin();
    entityManager.persist(new User("Monica", "monica@domain.com"));
    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

5.结论

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.

在这篇文章中,我们展示了如何使用JPA的PersistenceUnitInfo接口和Hibernate的EntityManagerFactoryBuilderImpl类,以编程方式引导JPA实体管理器,而无需依赖传统的“persistence.xml”文件

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

像往常一样,本文中显示的所有代码样本都可以在GitHub上获得