【问题标题】:Spring Boot voodoo required instantiating JPA with DataNucleus and HikariSpring Boot voodoo 需要使用 DataNucleus 和 Hikari 实例化 JPA
【发布时间】:2017-06-01 10:46:03
【问题描述】:

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

我正在尝试从 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() 确实有帮助。

另外,我怀疑这个错误

需要一个 bean,但找到了 2 个

或者建议的操作很有帮助 - 我不想进入 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,错误消失了。

【问题讨论】:

  • 不知道 Spring 的所有内容,但 LocalContainerEntityManagerFactoryBean.setPersistenceProviderClass 没有定义您的 JPA 提供程序,因此您可能不必从 CLASSPATH 中排除其他提供程序?毕竟,DataNucleus 在拥有“提供者”方面只是遵守 JPA 规范。
  • 什么是“坚持自己版本的持久化 API”?你在这里没有给出任何版本。我在他们的“API jar”中看不到任何不在 JPA 2.1 规范中的内容。显然,JPA 专家组没有发布标准的 JPA API,因此提供者都创建了自己的 API jar。
  • @NeilStockton DataNucleus 不必有 persistence.xml,是吗?
  • DataNucleus 支持完整的 JPA,因此您提供 persistence.xml,就像 JPA 规范告诉您的那样。显然 Spring 可能正在做其他事情来生成没有 persistence.xml 的 EMF,方法是传入 PersistenceUnitInfo
  • 可能是this post 会有所帮助,或者this

标签: spring jpa spring-boot spring-data-jpa datanucleus


【解决方案1】:

我把LocalContainerEntityManagerFactoryBean方法名改成了entityManagerFactory

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

要启用测试,我必须复制这个@Configuration 类并更改 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 方面抱怨这里有 2 个候选注入。

我还发现,使用此配置,EntityManager 的存储库/DTO 依赖注入不适用于@Autowired。它必须是原生 JPA 注释:

@PersistenceContext
private EntityManager entityManager;

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

这为我的 Spring 牛肉增添了更多燃料。它经常不按照锡上所说的那样做。 Spring 测试应该在包层次结构中找到@Configuration 类,但没有——我需要使用@Import。 Spring 还应该根据类型(EntityManagerDataSource 等)找到依赖注入候选对象,但它不会——在某些情况下,它们必须由命名为特定名称的方法或使用声明名称的 @Bean 注释生成。

不过,它已经完成了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-29
    • 2020-05-17
    • 2022-01-23
    • 2018-10-05
    • 1970-01-01
    • 2019-10-28
    • 1970-01-01
    • 2018-10-05
    相关资源
    最近更新 更多