Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Context

I want to use in the same Spring context two different databases that have entities that share the same name, but not the same structure. I rely on Spring Data MongoDB and JPA/JDBC. I have two packages, containing among others the following files:

  • com.bar.entity
    • Car.class
  • com.bar.repository
    • CarRepository.class
    • RepoBarMarker.class
  • com.bar.config
    • MongoConfiguration.class
  • com.foo.entity
    • Car.class
  • com.foo.repository
    • CarRepository.class
    • RepoFooMarker.class
  • com.foo.config
    • JPAConfiguration.class
    • SpecEntityManagerFactory.class

The content of each Car.class is different, I cannot reuse them. bar uses Spring-Mongo and foo uses Spring-JPA, and repositories are initialised via @EnableMongoRepositories and @EnableJpaRepositories annotations. When in one of my application component I try to access the foo version of the repository:

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

I have the following exception when the class containing the @Resource field is created:

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

It appears that Spring tries to convert a bar repository to a foo repository, instead of creating a new bean, as in the same stack I also have the following exception:

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

If I try instead to autowire the repository:

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

I get the following exception:

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-data configuration

In foo (JPA) package, 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;
    }

}

In bar (MongoDB) package, 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;
    }
}

Question

If I change in foo repository the entity name to CarFoo.class and the repository to CarFooRepository.class, then everything works. But is there away to avoid renaming them and still have a real wiring per type, instead of name (as it is what seems to be done here), for Spring Data repositories?

like image 298
Nicolas Avatar asked Sep 22 '14 11:09

Nicolas


People also ask

Can we have two repositories in spring boot?

You can only have one repository per entity... however, you can have multiple entities per table; thus, having multiple repositories per table.

Which is better CrudRepository or JpaRepository?

Crud Repository doesn't provide methods for implementing pagination and sorting. JpaRepository ties your repositories to the JPA persistence technology so it should be avoided. We should use CrudRepository or PagingAndSortingRepository depending on whether you need sorting and paging or not.

What is the difference between a CrudRepository and a JpaRepository What is the difference between a CrudRepository and a JpaRepository?

Their main functions are: CrudRepository mainly provides CRUD functions. PagingAndSortingRepository provides methods to do pagination and sorting records. JpaRepository provides some JPA-related methods such as flushing the persistence context and deleting records in a batch.

What are different repositories in spring boot?

Each of these defines its own functionality: CrudRepository provides CRUD functions. PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.


1 Answers

In your case, you can use

@Repository("fooCarRepository")

on the interface declaration of

com.foo.repository.CarRepository

Although when using Spring Data @Repository is not generally needed on the interface, however in your case you need to supply it. That's because you need to make Spring register the implementation of the bean with a custom name (in this case fooCarRepository) in order to avoid the name collision.

like image 155
geoand Avatar answered Jan 02 '23 11:01

geoand