0

I am trying to set up a second data source for my project. This is the spring-context.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="PERSISTENCECLASSES"/>

    <!-- ************ JPA configuration *********** -->
    <tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="firstTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="firstEntityManagerFactory" />
    </bean>
    <bean id="secondTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="secondEntityManagerFactory" />
    </bean>
    <bean id="firstEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:/persistence.xml" />
        <property name="persistenceUnitName" value="second" />
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="DOMAINCLASSES" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
            </bean>
        </property>
    </bean>
    <bean id="secondEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
        <property name="persistenceUnitName" value="second" />
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="DOMAIN CLASSES" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
            </bean>
        </property>
    </bean>

    <bean id="userRepositoryImpl" class="PERSISTENCECLASSES.UserRepostoryImpl"/>

    <bean id="first" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:comp/env/jdbc/first" />
        <property name="resourceRef" value="true" />
    </bean>
    <bean id="second" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:comp/env/jdbc/second" />
        <property name="resourceRef" value="true" />
    </bean>
</beans>

My userRepo looks the following way:

public interface UserRepository extends JpaRepository<User, String>, UserRepositoryCustom {
    Optional<User> findUserByEMail(String eMail);
    Optional<User> findUserByUserName(String userName);
    Optional<User> findUserByUserNameOrEMail(String userName, String eMail);
}
public interface UserRepositoryCustom {}
public class UserRepositoryImpl extends QuerydslRepositorySupport implements UserRepositoryCustom {

    public UserRepositoryImpl() {
        super(User.class);
    }

    // Some QueryDSL methods
}

I am getting the following exception:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'loadStartupData': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'PERSISTENCECLASSES.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1395)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
    ... 24 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'PERSISTENCECLASSES.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1654)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 42 more

The LoadStartupData class is a @Servicethat creates an instance of UserRepository with the following call:

@Autowired UserRepository userrepo;

My Spring App:

@Configuration
@EnableScheduling
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableTransactionManagement
public class App {

    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException, UnirestException {

        SpringApplication.run(App.class, args);

    }

}

Any idea how to fix it or even a hint on where that might be coming from is highly appreciated? I tried using @PersistenceContext, Qualifiers and other things. -nothing worked though.

coderprt
  • 11
  • 6
  • really ? a typo in spring-conf.xml ? Also why is there a second interface ? what are you trying to achieve? – AntJavaDev Nov 25 '19 at 23:31
  • @AntJavaDev Where do you find a typo? I am trying to set up 2 databases where each database has its own repositories and domain classes. The UserRepoCustom has a set of methods that the UserRepoImpl implements. I let them out for breavity. – coderprt Nov 25 '19 at 23:51
  • are you sure this bean exists `` ?? Also the question about the second interface, is that you are trying to autowire the first one , without implementing it, i guess i am missing something – AntJavaDev Nov 26 '19 at 02:46
  • @AntJavaDav The second interface is extended by the first one. So User Repo extends UserRepoCustom. As far as I know, this is quite common to do. Plus, when I had only one data source that Spring autoconfigured, it worked that way so I guess that is not the problem. Regarding the bean, I followed a tutorial: https://www.codepedia.org/ama/how-to-setup-multiple-data-sources-with-spring-and-jpa/ where they had for each Repository an own bean in the spring-context.xml – coderprt Nov 26 '19 at 07:58
  • hmm interesting, so you are suggesting that the actual example works with the original datasource and the bean definition is been picked up because you are extending the same class. It would make sense if you were autowiring `UserRepositoryCustom`, which is the actual root cause, if you carefully read the logs. Also the attempt to manually register the bean will still fail as i believe the bean's class name is `PERSISTENCECLASSES.UserRepositoryImpl` and not `PERSISTENCECLASSES.USerRepostoryImpl` – AntJavaDev Nov 26 '19 at 09:21
  • @AntJavaDev Yes absolutely. Sorry the typo is not in the code itself but just here. I fixed the typo. – coderprt Nov 26 '19 at 09:38

1 Answers1

0

It looks like there is a confusion with the actual usage of Spring DATA(case before introducing a second datasource), and in the new case which the actual repository has to be manual implemented to serve the actual crud operations.

There are actually 2 options :

i) Half of your posted attempt, in which you will have to manually implement the actual query calls for every operation you need so you cant really use the JPARepository offered by Spring DATA. In the actual impl Class , you will autowire the 2 datasources and provide the actual service calls.

In reality the impl class would look like below

public class UserRepositoryImpl extends QuerydslRepositorySupport implements UserRepository {


@PersistenceContext(name = "firstEntityManagerFactory")
EntityManager firstEntityManagerFactory;
@PersistenceContext(name = "secondEntityManagerFactory")
EntityManager secondEntityManagerFactory;


//manually implement all crud operations , insert, delete, update, fetchByQuery plus your interface methods

Optional<User> findUserByEMail(String eMail);
Optional<User> findUserByUserName(String userName);
Optional<User> findUserByUserNameOrEMail(String userName, String eMail);
}

And your Interface would look like :

public interface UserRepository extends UserRepositoryCustom {
    Optional<User> findUserByEMail(String eMail);
    Optional<User> findUserByUserName(String userName);
    Optional<User> findUserByUserNameOrEMail(String userName, String eMail);
}

Also some typos, as already mentioned, should be corrected in the spring-conf.xml.(remove unused bean definitions, map to the proper entity-packages)

ii) Check this repository which distinguish the datasource by each entity (if that suits your case)

AntJavaDev
  • 1,204
  • 1
  • 18
  • 24
  • Thanks for the extensive reply! Why can’t I use JPARepositories with multiple data sources? Or are you suggesting that you can only use JPA or CRUD Repositories if you don’t have custom queries? – coderprt Nov 26 '19 at 10:29
  • And also: How would the UserRepo custom interface look like since you are defining custom queries now in the UserRepo Interface? – coderprt Nov 26 '19 at 11:16
  • if you check the second link, you would see that the `JPARepository<>` is actual tight with the specific `LocalContainerEntityManagerFactoryBean` by the 'managed' entities, which can use only one datasource(at least i am not aware of any other valid way). Imagine a single case in which you want to insert a new entity through the first datasource but update or fetch from the second !?! How would Spring know that to pass the actual implementation !? also check those answers [here](https://stackoverflow.com/questions/26308035/spring-boot-spring-data-jpa-with-multiple-datasources) – AntJavaDev Nov 26 '19 at 12:02
  • to answer your second question, in case your app is already invoking the spring data crud api, then you have to manually override almost every method(which can be painful) that is being defined to JPARepository<>, otherwise you will implement your own API, which will be passed to the service layer. Still it depends on what you are trying to achieve!? Maybe just proper config? Some Repository overrides [here](https://stackoverflow.com/questions/13036159/spring-data-override-save-method) – AntJavaDev Nov 26 '19 at 12:13