Spring Boot voodoo 需要使用 DataNucleus 和 Hikari 实例化 JPA

Spring Boot voodoo required instantiating JPA with DataNucleus and Hikari

欢迎任何使此配置正常工作的帮助。

我正在尝试从 Spring Boot 接管自动连接池、数据源和 JPA 配置,以允许我将 DataNucleus 而不是 Hibernate

我的方法是在试错的基础上对 Boot 说缺失的部分进行编码。我不得不删除 Hibernate 依赖项以允许 DataNucleus 运行.

也许我现在编码太多或者我还不够。

Spring 因错误而失败:

Exception encountered during context initialization - cancelling refresh attempt: 
[huge SNIP]
nested exception is org.springframework.beans.BeanInstantiationException: 
    Failed to instantiate [org.springframework.data.repository.support.Repositories]: 
    Factory method 'repositories' threw exception; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
    Error creating bean with name 'symbolRepositoryImpl': 
    Unsatisfied dependency expressed through field 'entityManager'; 
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
    No qualifying bean of type 'javax.persistence.EntityManager' available: 
    expected single matching bean but found 2:
    org.springframework.orm.jpa.SharedEntityManagerCreator#0,
    org.springframework.orm.jpa.SharedEntityManagerCreator#1
[SNIP]
2017-06-01 09:43:09.675 ERROR 9108 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field entityManager in com.bp.gis.tardis.repository.SymbolRepositoryImpl 
    required a single bean, but 2 were found:
    - org.springframework.orm.jpa.SharedEntityManagerCreator#0: defined by method 'createSharedEntityManager' in null
    - org.springframework.orm.jpa.SharedEntityManagerCreator#1: defined by method 'createSharedEntityManager' in null


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, 
or using @Qualifier to identify the bean that should be consumed

我可以花几个小时进一步调试,但断点出现在应该注入 entityManager 的存储库之一的初始化中。

这是我手动实例化的:

@Configuration
@EnableJpaRepositories(
        basePackages = {"org.adam.repository"}
)
public class DataSourceConfig {


    @Bean
    @ConfigurationProperties(prefix = "adam.datasource")
    public AdamDataSourceProperties getDataSourceProperties() {
        return new AdamDataSourceProperties();
    }

    @Bean
    public DataSource getDataSource() {
        AdamDataSourceProperties props = getDataSourceProperties();
        return new HikariDataSource(props.getHikariConfig());
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean getEmfBean() {
        LocalContainerEntityManagerFactoryBean emfBean =
                new LocalContainerEntityManagerFactoryBean();
        emfBean.setDataSource(getDataSource());
        emfBean.setPersistenceUnitName("adam");
        return emfBean;
    }

    @Bean
    public EntityManagerFactory getEmf() {
        LocalContainerEntityManagerFactoryBean emfBean = getEmfBean();
        return emfBean.getNativeEntityManagerFactory();
    }

}

我的 AdamDatasourceProperties 由 Spring 使用 application.properties 中的 "adam.datasource" 前缀值初始化,然后它可以创建一个 HikariConfig 对象以用于实例化 HikariDataSource。这一点实际上很好,可能是实体管理器工厂导致了问题——或者其他问题。

我没有证据表明我的最后一个方法 getEmf() 确实有用。

此外,我怀疑错误

Required a single bean, but 2 were found

或者建议的操作很有帮助 - 我不想进入 Spring 源代码以便将 Spring 的 SharedEntityManagerCreator 上的其中一种方法注释为 @Primary.

更新 DataNucleus 不会 运行 如果它在类路径上找到其他 JPA API 类 - 它坚持自己的持久性版本 API - 因此删除需要 Hibernate 包。

Caused by: org.datanucleus.exceptions.NucleusUserException: 
    Found Meta-Data for class org.adam.entity.TimeSeriesEntity 
    but this class is either not enhanced or you have multiple copies 
    of the persistence API jar in your CLASSPATH!! 
    Make sure all persistable classes are enhanced before running 
    DataNucleus and/or the CLASSPATH is correct.
    at org.datanucleus.metadata.MetaDataManagerImpl
             .initialiseClassMetaData(MetaDataManagerImpl.java:2814)

所以我从 spring-boot-starter-data-jpa 中排除了 Hibernate,错误消失了。

我将 LocalContainerEntityManagerFactoryBean 方法名称更改为 entityManagerFactory:

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean emfBean =
            new LocalContainerEntityManagerFactoryBean();
    emfBean.setDataSource(getDataSource());
    emfBean.setPersistenceUnitName("adam");
    return emfBean;
}

为了启用测试,我必须复制此 @Configuration class 并更改 EMF 方法以接受 Spring 的测试数据库:

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
        @Qualifier("dataSource") DataSource dataSource) {
    LocalContainerEntityManagerFactoryBean emfBean =
            new LocalContainerEntityManagerFactoryBean();
    emfBean.setDataSource(dataSource);
    emfBean.setPersistenceUnitName("adam");
    return emfBean;
}

@Qualifier 是为了 Intellij,其 Spring facet 抱怨这里有 2 个候选注入。

我还发现,使用此配置,EntityManager 的 repository/DTO 依赖注入不适用于 @Autowired。它必须是本机 JPA 注释:

@PersistenceContext
private EntityManager entityManager;

使用我之前的 Hibernate 和 OpenJPA 配置,Spring 很高兴在 @Autowire 存在的情况下注入自己的实例化 EntityManager

这让我对 Spring 感到不满。它经常不按照罐头上说的做。 Spring 测试应该在包层次结构中找到 @Configuration classes,但没有——我需要使用 @Import。 Spring 还应该根据类型(EntityManagerDataSource 等)找到依赖注入候选者,但它不会——在某些情况下,它们必须由命名为特定名称的方法或使用@Bean 声明名称的注释。

不过,完成了。