Spring 数据 : inject 2 repositories with same name but in 2 different packages

标签 spring spring-data-jpa spring-data spring-data-mongodb

上下文

我想在同一个 Spring 上下文中使用两个不同的数据库,它们的实体共享相同的名称,但结构不同。我依赖 Spring Data MongoDB 和 JPA/JDBC。我有两个包,其中包含以下文件:

  • com.bar.entity
    • 汽车类别
  • com.bar.repository
    • CarRepository.class
    • RepoBarMarker.class
  • com.bar.config
    • MongoConfiguration.class
  • com.foo.entity
    • 汽车类别
  • com.foo.repository
    • CarRepository.class
    • RepoFooMarker.class
  • com.foo.config
    • JPAConfiguration.class
    • SpecEntityManagerFactory.class

每个Car.class的内容都不同,我无法重用它们。 bar 使用 Spring-Mongo,foo 使用 Spring-JPA,存储库通过 @EnableMongoRepositories 和 @EnableJpaRepositories 注解初始化。在我的应用程序组件之一中,我尝试访问存储库的 foo 版本:

@Resource
private com.foo.repository.CarRepository carRepository;

创建包含 @Resource 字段的类时出现以下异常:

Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'carRepository' must be of type [com.foo.repository.CarRepository], but was actually of type [com.sun.proxy.$Proxy31]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:374)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:446)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:420)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:545)
    at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:155)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:305)
    ... 26 more

看来Spring试图将bar存储库转换为foo存储库,而不是创建一个新的bean,因为在同一个堆栈中我也有以下异常:

Caused by: java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy31 implementing com.bar.repository.CarRepository,org.springframework.data.repository.Repository,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.foo.repository.CarRepository]: no matching editors or conversion strategy found
        at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:267)
        at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93)
        at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
        ... 35 more

如果我尝试 Autowiring 存储库:

@Autowire
private com.foo.repository.CarRepository carRepository;

我收到以下异常:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.foo.CarRepository com.shell.ShellApp.carRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.foo.CarRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:509)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:290)
    ... 26 more

Spring 数据配置

foo (JPA) 包中,JPAConfigration.class:

@Configuration
@EnableJpaRepositories(basePackageClasses = RepoFooMarker.class)
public class JPAConfiguration {

    @Autowired
    public DataSource dataSource;

    @Autowired
    public EntityManagerFactory entityManagerFactory;

    @Bean
    public EntityManager entityManager(final EntityManagerFactory entityManagerFactory) {
        return entityManagerFactory.createEntityManager();
    }

    @Bean
    public Session session(final EntityManager entityManager)
    {
        return entityManager.unwrap(Session.class);
    }

    @Bean
    public PlatformTransactionManager transactionManager() throws SQLException {

        final JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }
}

SpecEntityManagerFactory.class:

@Configuration
public class SpecEntityManagerFactory {

    @Bean
    public EntityManagerFactory entityManagerFactory(final DataSource dataSource) throws SQLException {

        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);
        vendorAdapter.setDatabase(Database.POSTGRESQL);

        final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("com.foo.entity");
        factory.setJpaProperties(getHibernateProperties());
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();

        return factory.getObject();
    }

    private Properties getHibernateProperties()
    {
        final Properties hibernateProperties = new Properties();
        hibernateProperties.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");

        return hibernateProperties;
    }

}

bar (MongoDB) 包中,MongoConfiguration.class:

@Configuration
@EnableMongoRepositories(basePackageClasses = RepoBarMarker.class)
public class MongoConfiguration extends AbstractRepoConfig {

    @Override
    @Bean
    public MongoOperations mongoTemplate() {

        final MongoClient mongo = this.getMongoClient();
        final MongoClientURI mongoUri = this.getMongoClientUri();

        final MongoTemplate mongoTemplate = new MongoTemplate(mongo, mongoUri.getDatabase());
        mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());
        mongoTemplate.setWriteConcern(WriteConcern.UNACKNOWLEDGED);

        return mongoTemplate;
    }
}

问题

如果我将 foo 存储库中的实体名称更改为 CarFoo.class,将存储库更改为 CarFooRepository.class,那么一切正常。但是,对于 Spring Data 存储库,有没有办法避免重命名它们,并且仍然为每种类型提供真正的连接,而不是名称(因为这似乎是这里所做的)?

最佳答案

根据您的情况,您可以使用

@Repository("fooCarRepository")

关于接口(interface)声明

com.foo.repository.CarRepository

尽管在使用 Spring Data 时@Repository 在接口(interface)上通常不需要,但是在您的情况下您需要提供它。这是因为您需要让 Spring 使用自定义名称(在本例中为 fooCarRepository)注册 bean 的实现,以避免名称冲突。

关于 Spring 数据 : inject 2 repositories with same name but in 2 different packages,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25973356/

相关文章:

java - 无法访问 Spring Controller

javascript - 使用 Javascript 动态实现 Spring Security

java - 标准API。带有 OrderBy 表达式的谓词

java - 将架构名称添加到 Spring 数据中的实体?

java - 在不重启服务器的情况下动态重新加载 Spring Mongo 数据库连接

java - Spring Security 未捕获 ApplicationListener 中的登录失败

java - 如何告诉 Hibernate 在运行 JUnit 测试时不要存储数据?

spring - 如何使用 Kotlin 正确查询 Spring JPA 中的实体

java - 基于当前日期+时间查询 JPA 实体实例的最佳方法是什么?

java - 自定义查询不适用于 Spring Data Neo4j 存储库中的界面投影