【问题标题】:Create JPA EntityManager without persistence.xml configuration file创建没有 persistence.xml 配置文件的 JPA EntityManager
【发布时间】:2023-04-08 04:57:01
【问题描述】:

有没有办法在没有定义持久性单元的情况下初始化EntityManager?您能否提供创建实体管理器所需的所有属性?我需要在运行时根据用户指定的值创建EntityManager。更新 persistence.xml 并重新编译不是一种选择。

欢迎任何关于如何做到这一点的想法!

【问题讨论】:

    标签: jpa runtime entitymanager persistence.xml


    【解决方案1】:

    使用普通 JPA,假设您有一个 PersistenceProvider 实现(例如 Hibernate),您可以使用 PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) 方法引导 EntityManagerFactory 而不需要 persistence.xml

    但是,您必须实现 PersistenceUnitInfo 接口很烦人,因此您最好使用 Spring 或 Hibernate,它们都支持在没有 persistence.xml 文件的情况下引导 JPA:

    this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
        this.persistenceUnitInfo, 
        getJpaPropertyMap()
    );
    

    PersistenceUnitInfo 由 Spring 特定的 MutablePersistenceUnitInfo 类实现。

    【讨论】:

    • 使用MutablePersistenceUnitInfo 不适用于某些方法throw UnsupportedOperationException。另外提到的article 有点过时了:getPersistenceUnitRootUrl 不能返回 null 否则 Hibernate 不会扫描类路径(Hibernate 5.2.8)。
    • 我有点错了,这篇文章在这方面并没有过时,因为代码正在传递实体列表并且不使用包扫描。然而,对于自动实体扫描,必须实现getPersistenceUnitRootUrl getJarFileUrls。后者播在stackoverflow.com/a/42372648/48136
    【解决方案2】:

    您也可以使用 PersistenceContext 或 Autowired 注释获取 EntityManager,但请注意它不是线程安全的。

    @PersistenceContext
    private EntityManager entityManager;
    

    【讨论】:

      【解决方案3】:

      我使用的 DataNucleus JPA 也有办法做到这一点in its docs。不需要 Spring,也不需要 PersistenceUnitInfo 的丑陋实现。

      只需如下操作

      import org.datanucleus.metadata.PersistenceUnitMetaData;
      import org.datanucleus.api.jpa.JPAEntityManagerFactory;
      
      PersistenceUnitMetaData pumd = new PersistenceUnitMetaData("dynamic-unit", "RESOURCE_LOCAL", null);
      pumd.addClassName("mydomain.test.A");
      pumd.setExcludeUnlistedClasses();
      pumd.addProperty("javax.persistence.jdbc.url", "jdbc:h2:mem:nucleus");
      pumd.addProperty("javax.persistence.jdbc.user", "sa");
      pumd.addProperty("javax.persistence.jdbc.password", "");
      pumd.addProperty("datanucleus.schema.autoCreateAll", "true");
      
      EntityManagerFactory emf = new JPAEntityManagerFactory(pumd, null);
      

      【讨论】:

        【解决方案4】:

        这是一个没有 Spring 的解决方案。 常量取自org.hibernate.cfg.AvailableSettings

        entityManagerFactory = new HibernatePersistenceProvider().createContainerEntityManagerFactory(
                    archiverPersistenceUnitInfo(),
                    ImmutableMap.<String, Object>builder()
                            .put(JPA_JDBC_DRIVER, JDBC_DRIVER)
                            .put(JPA_JDBC_URL, JDBC_URL)
                            .put(DIALECT, Oracle12cDialect.class)
                            .put(HBM2DDL_AUTO, CREATE)
                            .put(SHOW_SQL, false)
                            .put(QUERY_STARTUP_CHECKING, false)
                            .put(GENERATE_STATISTICS, false)
                            .put(USE_REFLECTION_OPTIMIZER, false)
                            .put(USE_SECOND_LEVEL_CACHE, false)
                            .put(USE_QUERY_CACHE, false)
                            .put(USE_STRUCTURED_CACHE, false)
                            .put(STATEMENT_BATCH_SIZE, 20)
                            .build());
        
        entityManager = entityManagerFactory.createEntityManager();
        

        还有臭名昭著的PersistenceUnitInfo

        private static PersistenceUnitInfo archiverPersistenceUnitInfo() {
            return new PersistenceUnitInfo() {
                @Override
                public String getPersistenceUnitName() {
                    return "ApplicationPersistenceUnit";
                }
        
                @Override
                public String getPersistenceProviderClassName() {
                    return "org.hibernate.jpa.HibernatePersistenceProvider";
                }
        
                @Override
                public PersistenceUnitTransactionType getTransactionType() {
                    return PersistenceUnitTransactionType.RESOURCE_LOCAL;
                }
        
                @Override
                public DataSource getJtaDataSource() {
                    return null;
                }
        
                @Override
                public DataSource getNonJtaDataSource() {
                    return null;
                }
        
                @Override
                public List<String> getMappingFileNames() {
                    return Collections.emptyList();
                }
        
                @Override
                public List<URL> getJarFileUrls() {
                    try {
                        return Collections.list(this.getClass()
                                                    .getClassLoader()
                                                    .getResources(""));
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                }
        
                @Override
                public URL getPersistenceUnitRootUrl() {
                    return null;
                }
        
                @Override
                public List<String> getManagedClassNames() {
                    return Collections.emptyList();
                }
        
                @Override
                public boolean excludeUnlistedClasses() {
                    return false;
                }
        
                @Override
                public SharedCacheMode getSharedCacheMode() {
                    return null;
                }
        
                @Override
                public ValidationMode getValidationMode() {
                    return null;
                }
        
                @Override
                public Properties getProperties() {
                    return new Properties();
                }
        
                @Override
                public String getPersistenceXMLSchemaVersion() {
                    return null;
                }
        
                @Override
                public ClassLoader getClassLoader() {
                    return null;
                }
        
                @Override
                public void addTransformer(ClassTransformer transformer) {
        
                }
        
                @Override
                public ClassLoader getNewTempClassLoader() {
                    return null;
                }
            };
        }
        

        【讨论】:

        • 这对我帮助很大,因为它帮助我避免了在某些测试用例中使用 arquillian 的开销!
        【解决方案5】:

        有没有办法在没有定义持久单元的情况下初始化EntityManager

        您应该在persistence.xml 部署描述符中定义至少一个持久性单元。

        您能否提供创建Entitymanager 所需的所有属性?

        • name 属性是必需的。其他属性和元素是可选的。 (JPA 规范)。所以这应该或多或少是你最小的persistence.xml 文件:
        <persistence>
            <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
                SOME_PROPERTIES
            </persistence-unit>
        </persistence>
        

        在 Java EE 环境中,jta-data-sourcenon-jta-data-source 元素用于指定 JTA 和/或非 JTA 数据源的全局 JNDI 名称以供持久性提供程序使用.

        因此,如果您的目标应用程序服务器支持 JTA(JBoss、Websphere、GlassFish),您的 persistence.xml 看起来像:

        <persistence>
            <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
                <!--GLOBAL_JNDI_GOES_HERE-->
                <jta-data-source>jdbc/myDS</jta-data-source>
            </persistence-unit>
        </persistence>
        

        如果您的目标应用服务器不支持 JTA (Tomcat),您的 persistence.xml 看起来像:

        <persistence>
            <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
                <!--GLOBAL_JNDI_GOES_HERE-->
                <non-jta-data-source>jdbc/myDS</non-jta-data-source>
            </persistence-unit>
        </persistence>
        

        如果您的数据源未绑定到全局 JNDI(例如,在 Java EE 容器之外),那么您通常会定义 JPA 提供程序、驱动程序、url、用户和密码属性。 属性名称取决于 JPA 提供者。因此,对于作为 JPA 提供者的 Hibernate,您的 persistence.xml 文件将如下所示:

        <persistence>
            <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
                <provider>org.hibernate.ejb.HibernatePersistence</provider>
                <class>br.com.persistence.SomeClass</class>
                <properties>
                    <property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.ClientDriver"/>
                    <property name="hibernate.connection.url" value="jdbc:derby://localhost:1527/EmpServDB;create=true"/>
                    <property name="hibernate.connection.username" value="APP"/>
                    <property name="hibernate.connection.password" value="APP"/>
                </properties>
            </persistence-unit>
        </persistence>
        

        交易类型属性

        通常,在 Java EE 环境中,RESOURCE_LOCAL 的事务类型假定将提供非 JTA 数据源。在 Java EE 环境中,如果未指定此元素,则默认为 JTA。在 Java SE 环境中,如果未指定此元素,则可以假定默认值为 RESOURCE_LOCAL

        • 为确保 Java SE 应用程序的可移植性,有必要明确列出包含在持久性单元(JPA 规范)中的托管持久性类

        我需要在运行时根据用户指定的值创建EntityManager

        所以用这个:

        Map addedOrOverridenProperties = new HashMap();
        
        // Let's suppose we are using Hibernate as JPA provider
        addedOrOverridenProperties.put("hibernate.show_sql", true);
        
        Persistence.createEntityManagerFactory(<PERSISTENCE_UNIT_NAME_GOES_HERE>, addedOrOverridenProperties);
        

        【讨论】:

        • 您好,我尝试了您的解决方案,但遇到了问题,请您检查我的问题:stackoverflow.com/questions/3935394/…
        • 但是...问题是如何创建一个JPA EntityManager 没有 persistence.xml。这个答案不错,不过还是用persistence.xml吧?
        • 在 JavaEE 环境中,创建 EntityManagerFactory 是否由 EJB/JPA 管理?
        【解决方案6】:

        我能够使用 Hibernate 和 PostgreSQL 创建一个 EntityManager,纯粹使用以下 Java 代码(使用 Spring 配置):

        @Bean
        public DataSource dataSource() {
            final PGSimpleDataSource dataSource = new PGSimpleDataSource();
        
            dataSource.setDatabaseName( "mytestdb" );
            dataSource.setUser( "myuser" );
            dataSource.setPassword("mypass");
        
            return dataSource;
        }
        
        @Bean
        public Properties hibernateProperties(){
            final Properties properties = new Properties();
        
            properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect" );
            properties.put( "hibernate.connection.driver_class", "org.postgresql.Driver" );
            properties.put( "hibernate.hbm2ddl.auto", "create-drop" );
        
            return properties;
        }
        
        @Bean
        public EntityManagerFactory entityManagerFactory( DataSource dataSource, Properties hibernateProperties ){
            final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
            em.setDataSource( dataSource );
            em.setPackagesToScan( "net.initech.domain" );
            em.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
            em.setJpaProperties( hibernateProperties );
            em.setPersistenceUnitName( "mytestdomain" );
            em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
            em.afterPropertiesSet();
        
            return em.getObject();
        }
        

        LocalContainerEntityManagerFactoryBean.afterPropertiesSet() 的调用是必不可少的,否则工厂永远不会建成,然后getObject() 返回null,而你整天都在追逐NullPointerExceptions。 >:-(

        然后它使用以下代码:

        PageEntry pe = new PageEntry();
        pe.setLinkName( "Google" );
        pe.setLinkDestination( new URL( "http://www.google.com" ) );
        
        EntityTransaction entTrans = entityManager.getTransaction();
        entTrans.begin();
        entityManager.persist( pe );
        entTrans.commit();
        

        我的实体在哪里:

        @Entity
        @Table(name = "page_entries")
        public class PageEntry {
        
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private long id;
        
            private String linkName;
            private URL linkDestination;
        
            // gets & setters omitted
        }
        

        【讨论】:

        • Hibernate 的不错替代品。
        【解决方案7】:

        是的,您可以在 @Configuration 类(或其等效的 spring config xml)中使用这样的 spring 而不使用任何 xml 文件:

        @Bean
        public LocalContainerEntityManagerFactoryBean emf(){
            properties.put("javax.persistence.jdbc.driver", dbDriverClassName);
            properties.put("javax.persistence.jdbc.url", dbConnectionURL);
            properties.put("javax.persistence.jdbc.user", dbUser); //if needed
        
            LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
            emf.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); //If your using eclipse or change it to whatever you're using
            emf.setPackagesToScan("com.yourpkg"); //The packages to search for Entities, line required to avoid looking into the persistence.xml
            emf.setPersistenceUnitName(SysConstants.SysConfigPU);
            emf.setJpaPropertyMap(properties);
            emf.setLoadTimeWeaver(new ReflectiveLoadTimeWeaver()); //required unless you know what your doing
            return emf;
        }
        

        【讨论】:

        • properties是什么对象?
        • 它是一个简单的 java.util.Properties 对象
        猜你喜欢
        • 1970-01-01
        • 2011-11-30
        • 1970-01-01
        • 1970-01-01
        • 2014-02-22
        • 2011-11-07
        • 2014-05-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多