1

I've a class EntityLoader that's used to fetch some data from a MySQL database using Hibernate. But now the need is to fetch data from two different databases (MySQL and Oracle in this case). So I want to have two beans of EntityLoader but injecting a different SessionFactory in each one.

EntityLoader is defined as follows:

package com.demo

@Component
public class EntityLoader {

    @Autowired
    private SessionFactory sessionFactory;

    /* Code ... */

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }   
}

And the context configuration is:

<context:component-scan base-package="com.demo" />
<bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

So far it works fine. Over this I have done the following changes:

  • Exclude EntityLoader from component-scan in order to avoid the auto creation of a EntityLoader
  • Add mysqlSessionFactory and oracleSessionFactory bean definitions
  • Add mysqlEntityRepoLoader and oracleEntityRepoLoader bean definitions

Note that in mysqlEntityRepoLoader and oracleEntityRepoLoader I've added the attribute autowired="no" hoping that this would tell Spring to not autowire the SessionFactory and use the defined ref instead.

The resulting configuration is:

<context:component-scan base-package="com.demo">
    <context:exclude-filter type="regex" expression="com.demo.EntityLoader"/>
</context:component-scan>

<bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <!-- ... config ... -->
</bean>
<bean id="oracleSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <!-- ... config ... -->
</bean>

<bean id="mysqlEntityRepoLoader" class="com.dome.imserso.api.core.data.EntityRepoLoader" autowire="no">
    <property name="sessionFactory" ref="mysqlSessionFactory"/>
</bean> 
<bean id="oracleEntityRepoLoader" class="com.dome.imserso.api.core.data.EntityRepoLoader" autowire="no">
    <property name="sessionFactory" ref="oracleSessionFactory"/>
</bean> 

But it seems that Spring is trying first to autowire the SessionFactory in any case. I get the following error:

No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: mysqlSessionFactory,oracleSessionFactory

If I remove the @Autowired all works fine. But I would like to maintain it, as this code is part of a generic lib used for other apps where the usual case is to load only from one database.

Is there any way to accomplish it without removing the annotation?

Tobías
  • 6,142
  • 4
  • 36
  • 62
  • You can create a dummy bean named `sessionFactory`... – araknoid Jun 22 '15 at 12:36
  • Just remove the `@Component` annotation from your `EntityLoader`? You are manually creating the instances in XML (so no `@Component` necessary), and you are manually wiring in the session factory by calling the `setSessionFactory` method (so no `@Autowired` necessary). – David Lavender Jun 22 '15 at 12:40
  • If I remove the annotations then I'll need to configure this bean in XML in all the other apps that uses it. My intention is precisely to avoid that. – Tobías Jun 22 '15 at 12:51

2 Answers2

3

If you are able to modify your lib which contains EntityLoader, following these 2 step will do the trip :

  1. In EntityLoader make your @Autowired optional:

    @Autowired(required=false)

  2. In XML configuration, exclude mysqlSessionFactory and oracleSessionFactory beans from autowire candidates, add autowire-candidate="false" to each sessionFactory:

<bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" autowire-candidate="false">
    <!-- ... config ... -->
</bean>
<bean id="oracleSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" autowire-candidate="false">
    <!-- ... config ... -->
</bean>

Voilà!

Qianlong
  • 269
  • 2
  • 12
0

It's the component-scan that's causing problems. Your beans are already manually created, but Spring is trying to create another EntityLoader because the component scan is picking up your @Component annotation.

You can tell the component scan (for just your app) to ignore that class:

<context:component-scan ...>
    <context:exclude-filter expression="com\.demo\.EntityLoader" type="regex" />
</context:component-scan>
David Lavender
  • 8,021
  • 3
  • 35
  • 55
  • I'm already using the `exclude-filter` in `component-scan`. In any case, if I remove `@Component` but keep `@Autowired`, the problem persists. – Tobías Jun 22 '15 at 13:08
  • I think the problem is that spring autowires the property ignoring my xml configuration. As if the annotation has precedence over the xml. – Tobías Jun 22 '15 at 13:11