web-dev-qa-db-fra.com

Comment configurer plusieurs sources de données avec Spring et JPA

Dans notre application, nous souhaitons définir plusieurs sources de données avec Spring et JPA. Nous avons donc créé 2 entityManagerFactory, 2 sources de données et 2 gestionnaires de transactions. 

web.xml

 <param-value>
    /WEB-INF/a_spring.xml
    /WEB-INF/b_spring.xml
 </param-value>

Persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://Java.Sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://Java.Sun.com/xml/ns/persistence http://Java.Sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="db1" transaction-type="RESOURCE_LOCAL">
        <class>com.rh.domain.RcA</class>
    </persistence-unit>

      <persistence-unit name="db2" transaction-type="RESOURCE_LOCAL">
      <class>com.rh.domain.Rcb</class>
    </persistence-unit>
</persistence>

a_spring.xml  

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx" 
           xmlns:aop="http://www.springframework.org/schema/aop"
           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-2.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

      <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>  
      <bean id = "RcMaintenanceService" class="com.rh.services.RcAbcMaintenanceServiceImpl" autowire="byName" />

    <aop:config>
            <aop:pointcut id="rOperation" expression="execution(* com.rh.services.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="rOperation"/>
        </aop:config>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                   <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiName" value="Java:comp/env/jdbc/db1" />
        </bean> 
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"/>
            <property name="dataSource" ref="dataSource"/>
        </bean>

        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
               <property name="persistenceUnitName" value="db1" />     
            <property name="dataSource" ref="dataSource"/>
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
                    <property name="showSql" value="true"/>
                    <property name="generateDdl" value="false"/>
                    <property name="database" value="MYSQL" />
                    <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
                </bean>
            </property>
            <property name="jpaDialect">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
                </bean>
            </property>
        </bean>

Je déclare également une autre entityManagetFactory, Transaction Manager et dataSource sur b_spring.xml .

Erreur

L'initialisation du bean a échoué; exception imbriquée est org.springframework.beans.factory.NoSuchBeanDefinitionException: No Le bean unique de type [javax.persistence.EntityManagerFactory] est défini: haricot simple attendu mais trouvé 2 Causé par: org.springframework.beans.factory.NoSuchBeanDefinitionException: No Le bean unique de type [javax.persistence.EntityManagerFactory] est défini: haricot simple attendu mais trouvé 2 à org.springframework.beans.factory.BeanFactoryUtils.beanOfTypeIncludingAncestors (BeanFactoryUtils.Java:303) à org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory (PersistenceAnnotationBeanPostProcessor.Java:451) à org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory (PersistenceAnnotationBeanPostProcessor.Java:428) à org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor $ AnnotatedMember.resolveEntityManager (PersistenceAnnotationBeanPostProcessor.Java:582) à org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor $ AnnotatedMember.resolve (PersistenceAnnotationBeanPostProcessor.Java:553) à org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor $ AnnotatedMember.inject (PersistenceAnnotationBeanPostProcessor.Java:489)

16
Hoo

En cas de configuration de plusieurs sources de données, nous devons définir celle qui sera considérée comme source de données principale. nous pouvons spécifier qu'en utilisant l'annotation @Primary dans la configuration Java ou primary=true dans la configuration de bean XML.

Comme il y a deux gestionnaires d'entités en cours de création en XML, nous devons utiliser @Qualifier pour spécifier le bean à injecter où. Dans votre cas, quelque chose comme ça. 

@PersistenceContext(unitName = "db1")
public void setEntityManager(@Qualifier("entityManagerFactory") EntityManager entityMgr) {
    this.em = entityMgr;
}

pour la configuration XML, nous pouvons faire quelque chose comme ça

<bean id="BaseService" class="x.y.z.BaseService">
    <property name="em" ref="entityManagerFactory"/>
    <property name="em1" ref="entityManagerFactory1"/>
</bean>

<bean id = "RcMaintenanceService" class="com.rh.services.RcAbcMaintenanceServiceImpl" autowire="byName" parent="BaseService"/>
7
lucid

Le message d'erreur que vous avez publié indique que vous êtes en mode de câblage automatique pour un objet de type EntityManagerFactory. Aucun du code que vous avez montré jusqu'à présent ne contient une telle injection, ce qui implique que c'est probablement dans du code que vous n'avez pas encore posté.

Si vous deviez publier la trace complète de la pile de l'erreur, vous pourrez parcourir la pile pour voir quel bean contient la référence non satisfaisante à un objet EntityManagerFactory, ce qui vous permettrait de changer la façon dont vous faites référence. pour permettre une référence au haricot spécifique que vous voulez.

Mettre à jour

Sur la base des informations complémentaires que vous avez fournies (merci) et de Google, il semble que d'autres utilisateurs aient également constaté que la spécification d'un nom d'unité ne suffisait pas pour obtenir le bon EntityManager injecté. Plusieurs publications en ligne soutiennent la recommandation de @ lucid d'utiliser l'annotation @Qualifier pour que Spring sélectionne le bon haricot, mais malheureusement cette annotation a été introduite dans la version 2.5 et n'est donc disponible que si vous effectuez une mise à niveau. (Ce qui, étant donné l’âge du framework Spring que vous utilisez, est probablement une bonne idée, mais c’est une conversation séparée.)

Toutefois, plusieurs utilisateurs ont indiqué qu'une autre approche est disponible dans la version 2.0.5, utilisant un seul PersistenceUnitManager qui fait référence à plusieurs sources de données plutôt que plusieurs unités de persistance faisant chacune référence à une source de données unique. D'après les documents officiels de Spring: https://docs.spring.io/spring/docs/2.0.x/reference/orm.html#orm-jpa-multiple-pu .

Globalement, je vous suggérerais d'envisager de passer à une version de Spring datant de moins de 10 ans, ce qui vous permettrait de spécifier une annotation @Qualifier pour la réponse de @ lucid. Mais si cela n’est pas possible pour une raison quelconque, l’approche PersistenceUnitManager devrait vous permettre de le faire fonctionner dans Spring 2.0.5.

1
Tim

Avez-vous essayé de fournir les détails du package contenant votre bean EntityManagerFactory?

Vous pouvez fournir les détails du paquet en tant que propriété dans votre définition de bean -

<property name="packagesToScan" value="com.XX.XX.XX.XX" />

nouvelle propriété à ajouter dans ce bloc -

<bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
           <property name="persistenceUnitName" value="db2" />     
        <property name="dataSource" ref="dataSource1"/>
  <-- add here for both beans  -->

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
                <property name="showSql" value="true"/>
                <property name="generateDdl" value="false"/>
                <property name="database" value="MYSQL" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
            </bean>
        </property>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
            </bean>
        </property>
    </bean>

De plus, il vous manque la propriété persistenceXmlLocation -

 <property name="persistenceXmlLocation" value="***/persistence.xml" />
1
user8826113

Il y a plusieurs choses qui ne me vont pas. Le nom des setters ne correspondra pas aux noms de propriété, je pense que c'est important, la deuxième chose concerne l'héritage, certaines annotations ne fonctionnent parfois que dans des classes concrètes, pas dans des classes de base. Je voudrais essayer de changer le service de base comme suit.

public class BaseService {

@PersistenceContext(unitName = "db2")
private EntityManager em;

@PersistenceContext(unitName = "db1")
private EntityManager em1;

public EntityManager getEm() {
    return em;
}

protected EntityManager getEm2() {
    return em1;
}

public void setEm(EntityManager entityMgr) {
    this.em = entityMgr;
}

public void setEm1(EntityManager entityMgr) {
    this.em = entityMgr;
}
}

Si cela ne fonctionne pas, j'essaie probablement de supprimer la classe de base et de voir si je mets l'annotation sous la classe concrète pour que cela fonctionne, je le ferais en déplaçant simplement les annotations dans la classe RcAbcMaintenanceServiceImpl et en supprimant l'instruction extend qui hérite de le BaseService

J'ai aussi remarqué que l'annotation PersistenceContext avait un autre paramètre https://docs.Oracle.com/javaee/6/api/javax/persistence/PersistenceContext.html , de sorte que vous pouvez utiliser le nom également. correspond à l'identifiant sur la définition du haricot.

0
Koitoer