Spring : how to get multiple datasource with Gener

2020-06-23 07:44发布

问题:

i have a web application using Spring 3.1.1. We have a genericDao using JdbcTemplate. Datasource is injecting like this in the GenericDaoImpl.

public class GenericDaoImpl<T extends Serializable> implements GenericDao<T> {

protected Class<T> entityClass;

protected JdbcTemplate jdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

....

@Override
public List<T> findAll(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
    return jdbcTemplate.query(sql, mapper, args);
}

}

This is a simple DAO.

@Repository
public class ElementDaoImpl extends GenericDaoImpl<Element> implements ElementDao {

    private static ParameterizedRowMapper<Element> mapper = new ParameterizedRowMapper<Element>() {...};

    public List<Element> findChildren(int id) {
        sql = "SELECT....";
        return findAll(sql, mapper, new Object[] {id});
    }

}

For moment, with unique datasource, it is working perfectly. The applicationContext is configured with annotation.

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/...."/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>

Now i have to integrate a new DAO still using genericDao, but working on another database (so another datasource).

I use the @Transactionnal in service. And i read on spring documentation that we can give a qualifier to the transaction in order to select the good transaction manager.

So, i create a new datasource, a new transaction manager...

<bean id="firstDS" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/...."/>
</bean>

<bean id="secondDS" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/...."/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="firstDS"/>
</bean>

<bean id="txManagerSecond" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="secondDS"/>
    <qualifier value="txSecond"/>
</bean>

<tx:annotation-driven proxy-target-class="true"/>

and on my new Service, i have added the value to the @Transactionnal annotation :

 @Transactionnal("txSecond")

To resume, i have 4 classes to manage the new database : Interface for service, Implementation for service with @Transactionnal("txSecond"), Interface for DAO, Implementation for DAO based on genericDao which has a JdbcTemplate object created on an injected Datasource.

I created a Junit test, but for moment, i block on an exception : NoSuchBeanDefinitionException : no Unique bean of type javax.sql.DataSource is defined. Expected Single matching bean but found 2 (firstDs, SecondDs).

I think the pb is the genericDao but not sure.

How to manage that ?

Thank you.

回答1:

set manually your dataSource on each bean in your configuration file

<bean id="elementDao" class="ElementDaoImpl" autowire="byName">
   <property name="datasource" ref="datasource2">
</bean>

An other solution : play with the "alias" http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-java-bean-aliasing



回答2:

The problem is that while autowiring the datasource ,you have not specified any qualifier.

 @Autowired


public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);

}

So, if you need both your datasources in the same class, configure them using two different setter injections and provide appropriate qualifier in each case.



回答3:

By default autowired annotation maps by type of bean so one has to use qualifier("bean id") in order to let spring container know which bean to wire when multiple beans of the same type exist in config xml.



回答4:

There is more elegant solution than adding qualifiers.

Spring: how to hide bean from dependency injection?

Autowiring two beans implementing same interface - how to set default bean to autowire?