Search code examples
databasespring-bootconnectionvaadin8

Spring-boot & multiple database connections: autowire service does not work


I'm writing an Spring-boot application that needs to connect to at least 2 databases. I have 1 project per database in order to define their domains, 1 project per database in order to define their services and 1 Vaadin project for the UI.

 - a business domain entity sample
    @Entity
    @Table(name="T_PARAMETER")
    public class Parameter extends BaseIdEntity implements Serializable {

    @Column(name="par_cls")
    @NotNull
    private String parameterClass;

    @Column(name="par_cd")
    @NotNull
    private String parameterCode;

    @Column(name="par_lan")
    @NotNull
    private String language;

    @Column(name="par_sht_val")
    @NotNull
    private String parameterValueShort;

    @Column(name="par_lng_val")
    @NotNull
    private String parameterValueLong;

 - a authentication domain entity sample

    @Entity
    @Table(name="t_user", schema="authenticate")
    public class User extends BaseIdEntity implements Serializable {

    @Id
    @Column(name="user_cd")
    private String userCode;

    @Column(name="pwd")
    @NotNull
    private String password;

    @Column(name="new_pwd_req")
    @NotNull
    private boolean passwordRequired;

    @Column(name="acc_lck")
    @NotNull
    private boolean accountLocked;

There are repositories onto these 2 entities beans, they just extends the JpaRepository as hereunder:

public interface ParameterRepository extends JpaRepository<Parameter,Integer>{}
  • the services are defined as hereunder:

    @Service
    @Transactional(transactionManager="authenticateTransactionManager")
    public class ServiceParameterImpl implements ServiceParameter {
    
    private final static Logger log = LoggerFactory.getLogger(ServiceParameterImpl.class);
    
    @Autowired
    private ParameterRepository parameterRepository;
    
    @Override
    @Transactional(readOnly=true,transactionManager="authenticateTransactionManager")
    public List<Parameter> findParameterHeader(String filter) {
    

    .../...

  • The client application as:

    @SpringBootApplication
    @Configuration
    @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class
        , HibernateJpaAutoConfiguration.class
        , DataSourceTransactionManagerAutoConfiguration.class })
    @ComponentScan(
        basePackages= {
                "org.associative.ui"
                ,"org.associative.service"
                })
    @Import({AssociativityConfiguration.class, AuthenticateConfiguration.class})
    public class Application {
    
    private final static Logger log = LoggerFactory.getLogger(Application.class);
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    

    }

  • and configurations:

    @Configuration
    @EnableTransactionManagement
    @EntityScan(basePackages= "org.associative.domain.associativity")
    @EnableJpaRepositories(
        basePackages = "org.associative.domain.associativity.repository"
        , entityManagerFactoryRef = "associativityEntityManager"
        , transactionManagerRef = "associativityTransactionManager"
        )
        @ConfigurationProperties(prefix = "db.associativity")
        public class AssociativityConfiguration {
    
    private final static Logger log = LoggerFactory.getLogger(AssociativityConfiguration.class);
    
    @Autowired
    private Environment env;
    
    private final static String ASSOCIATIVITY_DRIVER_CLASS_NAME = "db.associativity.classname";
    private final static String ASSOCIATIVITY_URL = "db.associativity.connectionUrl";
    private final static String ASSOCIATIVITY_USERNAME = "db.associativity.username";
    private final static String ASSOCIATIVITY_PASSWORD = "db.associativity.password";
    
    private final static String HIBERNATE_DIALECT = "hibernate.dialect";
    
    @Bean(name = "associativityDataSource")
    public DataSource datasource() {
        DataSource dataSource = DataSourceBuilder.create()
                .driverClassName(env.getProperty(ASSOCIATIVITY_DRIVER_CLASS_NAME))
                .url(env.getProperty(ASSOCIATIVITY_URL))
                .username(env.getProperty(ASSOCIATIVITY_USERNAME))
                .password(env.getProperty(ASSOCIATIVITY_PASSWORD)).build();
    
        if (log.isTraceEnabled())
            log.trace(String.format("associativityConfiguration datasource:%s", dataSource.toString()));
    
        return dataSource;
    }
    
    @Bean(name = "associativityEntityManager")
    @PersistenceContext(unitName = "associativity")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("associativityDataSource") DataSource dataSource) {
    
        Map<String, Object> jpaProperties = new HashMap<>();
        jpaProperties.put(HIBERNATE_DIALECT, env.getProperty(HIBERNATE_DIALECT));
    
        LocalContainerEntityManagerFactoryBean em = builder.dataSource(dataSource)
                .packages("org.associative.domain.authenticate").persistenceUnit("pu_associativity").properties(jpaProperties)
                .build();
        em.setJpaPropertyMap(jpaProperties);
    
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(adapter); // not mandatory definition
    
        return em;
    }
    
    @Bean(name = "associativityTransactionManager")
    public PlatformTransactionManager associativityTransactionManager(
            @Qualifier("associativityEntityManager") EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
    

    }

  • and

    @Configuration
    @EnableTransactionManagement
    @EntityScan(basePackages= "org.associative.domain.authenticate")
    @EnableJpaRepositories(
        basePackages = "org.associative.domain.authenticate.repository"
        , entityManagerFactoryRef = "authenticateEntityManager"
        , transactionManagerRef = "authenticateTransactionManager"
        )
    @ConfigurationProperties(prefix="db.authenticate")
    public class AuthenticateConfiguration {
    
    private final static Logger log = LoggerFactory.getLogger(AuthenticateConfiguration.class);
    
    @Autowired
    private Environment env;
    
    private final static String AUTHENTICATE_DRIVER_CLASS_NAME= "db.authenticate.classname";
    private final static String AUTHENTICATE_URL = "db.authenticate.connectionUrl";
    private final static String AUTHENTICATE_USERNAME = "db.authenticate.username";
    private final static String AUTHENTICATE_PASSWORD = "db.authenticate.password";
    private final static String HIBERNATE_DIALECT = "hibernate.dialect";
    
    @Primary
    @Bean(name = "authenticateDataSource")
    public DataSource datasource() {
        DataSource dataSource = DataSourceBuilder.create()
                .driverClassName(env.getProperty(AUTHENTICATE_DRIVER_CLASS_NAME))
                .url(env.getProperty(AUTHENTICATE_URL))
                .username(env.getProperty(AUTHENTICATE_USERNAME))
                .password(env.getProperty(AUTHENTICATE_PASSWORD))
                .build();
    
        if ( log.isTraceEnabled()) log.trace(String.format("authenticateDataSource datasource:%s", dataSource.toString()));
    
        return dataSource;
    }
    
    @Primary
    @Bean(name="authenticateEntityManager")
    @PersistenceContext(unitName = "authenticate")
    //https://raymondhlee.wordpress.com/tag/enablejparepositories/
    public LocalContainerEntityManagerFactoryBean entityManagerFactory( 
            EntityManagerFactoryBuilder builder, @Qualifier("authenticateDataSource")DataSource dataSource) {
    
        Map<String,Object> jpaProperties = new HashMap<>();
        jpaProperties.put(HIBERNATE_DIALECT, env.getProperty(HIBERNATE_DIALECT));
    
        LocalContainerEntityManagerFactoryBean em = builder
                .dataSource(dataSource)
                .packages("org.associative.domain.authenticate")
                .persistenceUnit("pu_authenticate")
                .properties(jpaProperties)
                .build();
        em.setJpaPropertyMap(jpaProperties);
    
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(adapter); // not mandatory definition
    
        return em;
    }
    
    @Primary
    @Bean(name="authenticateTransactionManager")
    public PlatformTransactionManager authenticateTransactionManager(
            @Qualifier("authenticateEntityManager")EntityManagerFactory entityManagerFactory){
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
    

    }

    I'm facing an issue when a service is built by using autowiring in the construction of my client interface:

    @SpringUI public class ParameterListView extends CssLayout implements Serializable {

    private final static Logger log = LoggerFactory.getLogger(ParameterListView.class);
    
    @Autowired
    private ParameterController controller;
    
    @PostConstruct
    private void initView() {
        if ( log.isTraceEnabled() ) log.trace(String.format("initView:%s", "no param"));
    
        Grid<Parameter> grid = new Grid<>();
        this.addComponent(grid);
    
        grid.setItems(controller.getParameterHeader(""));
    
        grid.addColumn(Parameter::getParameterClass);
        grid.addColumn(Parameter::getParameterValueShort);
        grid.addColumn(Parameter::getParameterValueLong);
    
2017-12-01 14:20:07.151 ERROR o.s.b.SpringApplication Application startup failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'parameterControllerImpl': Unsatisfied

dependency expressed through field 'serviceParameter'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serviceParameterImpl': Unsatisfied dependency expressed through field 'parameterRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'parameterRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.associative.domain.associativity.Parameter

I already spent a lot of time in order to solve the multiple database connections because I was thinking this issue comes from a definition problem but I'm not sure now. So, what should I look to in order to solve this.

Thank you very much.


Solution

  • The last line of your stack trace is a clue: Not a managed type: class org.associative.domain.associativity.Parameter. Hibernate doesn't know about your Parameter entity.

    In the LocalContainerEntityManagerFactoryBean you set packages to scan to org.associative.domain.authenticate. Your Parameter entity is not under this package.

    This should fix the problem:

    .packages("org.associative.domain.authenticate", "org.associative.domain.associativity")