2010-12-12 4 views
1

J'ai deux bases de données, avec deux ensembles de configurations de ressort: Le niveau inférieur est CORE db, le niveau supérieur est APP db.Spring pour ouvrir automatiquement un autre gestionnaire de transactions?

Chaque db a son persistenceUnit, EntityManagerFactory, transactionManager, avec le nom db joint, tels que "entityManagerFactoryApp", "transactionManagerCore" ...

Maintenant, j'ai une classe de service, l'emballage des OTI dans l'APP , et certains dans CORE. Mais je trouve que je ne peux pas engager les OTI de base dans mon test:

Voici ma classe de service:

@Inject private AppDao appDao; 
    @Inject private CoreDao coreDao; 

    @Override 
    @Transactional 
    public void someMethod(foo bar) 
    { 
    appDao.save(...); //success  
    coreDao.save(...); //failed ! 
    } 

Et c'est ma classe de test:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"classpath:app.xml"}) 
@TransactionConfiguration(transactionManager="transactionManagerApp" , defaultRollback=false) 
public class ServiceTest 
{ 
    @Inject private Service service; 

    @Test 
    @Transactional 
    public void testSomeMethod() 
    { 
    service.someMethod(...); 
    } 
} 

Je sais que la raison pour laquelle je ne peux pas commettre Le DAO de CORE est dû au fait que la @TransactionConfiguration de la classe de test est "transactionManagerApp" et non "transactionManagerCore". Ainsi, toutes les actions CREATE/UPDATE/DELETE dans les DAO de CORE ne seront pas validées. Mais je ne peux pas activer deux txManagers simultanément (y at-il un moyen?).

Alors, je modifie ma classe de service:

@Inject 
    @Qualifier("entityManagerFactoryCore") 
    private EntityManagerFactory emfCore; 

    @Override 
    @Transactional 
    public void someMethod(foo bar) 
    { 
    appDao.save(...); //success  

    Session session = (Session) EntityManagerFactoryUtils.getTransactionalEntityManager(emfCore).getDelegate(); 
    Transaction tx = session.beginTransaction(); 
    coreDao.save(...); //success 
    tx.commit(); 
    } 

Oui, ça marche! Mais ce n'est pas ce que je veux! Parce qu'il introduit beaucoup de codes redondants (session, tx, commit ...).

Et ... il y a une autre façon, pour supprimer la session/EntityManagerFactoryUtils de service, et les déplacer dans la classe de test:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"classpath:app.xml"}) 
@TransactionConfiguration(transactionManager="transactionManagerApp" , defaultRollback=false) 
public class ServiceTest 
{ 
    @Inject private Service service; 

    @Inject 
    @Qualifier("entityManagerFactoryCore") 
    private EntityManagerFactory emfCore; 

    @Test 
    @Transactional 
    public void testSomeMethod() 
    { 
    Session session = (Session) EntityManagerFactoryUtils.getTransactionalEntityManager(emfCore).getDelegate(); 
    Transaction tx = session.beginTransaction(); 
    service.someMethod(...); 
    tx.commit(); 
    } 
} 

cela fonctionne aussi, mais il est le même trop laid!

Maintenant, ma question est, est-il possible pour le printemps pour ouvrir automatiquement le transactionManager lié (s) et début/fin tx? PS: J'ai remarqué ceci: 10.5.6.2 Multiple Transaction Managers with @Transactional, mais il semble ne pas remplir mon exigence: pour ouvrir un autre txManager dans ONE méthode.

environnements: printemps-3.0.5, mise en veille prolongée-3.6.0, JPA2

- mis à jour -

Merci @Bozho pour me dire d'appeler une nouvelle @Transactional (valeur = "txMgrName") méthode, j'ai essayé, mais toujours pas:

Voici mon code de service:

@Override 
    @Transactional 
    public void someMethod(foo bar) 
    { 
    appDao.save(...); //success 
    someCoreMethod(); 
    } 

    @Transactional(value="transactionManagerCore" , propagation=Propagation.REQUIRES_NEW) 
    private void someCoreMethod(...) 
    { 
    coreDao.save(...); //failed 
    } 

dans core.xml:

<bean id="transactionManagerCore" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactoryCore" /> 
    <qualifier value="transactionManagerCore"/> 
    </bean> 

Il reste a échoué, le coreDao sauve toujours rien. Je pense que c'est peut-être parce que la méthode est privée, et non interceptée par Spring. J'extrait la méthode dans le niveau d'interface/de mise en œuvre:

Service (interface) 
    public void someMethod(foo bar) 
    public void someCoreMethod(...) 

ServiceImpl (class) : unchanged 

Mais toujours pas! En fait, j'ai trouvé que le ressort ignore l'annotation @Transactional dans someCoreMethod().

Je peux même annoter @Transactional (valeur = "non-existence txManager nom") avec un MAUVAIS txManager, et le printemps ne signale aucune erreur (et rien engage)!

Ai-je raté quelque chose?

Répondre

2

Vous pouvez le faire via xml - <aop:config> (il y a un exemple dans les documents que vous avez liés). Il va créer deux proxies autour de l'objet, et donc les 2 transactions seront validées. C'est une histoire différente s'il s'agit d'une bonne pratique.

Une autre option est d'invoquer une nouvelle méthode (dans une nouvelle classe) qui a

@Transactional(propagation=REQUIRES_NEW, "anotherTransactionManager") 
+0

* Il est une autre histoire que ce soit une bonne pratique * une histoire tout à fait différente, oui ... :-) bonne réponse btw –

+0

Merci, j'ai essayé, mais toujours échoué. Pourriez-vous jeter un coup d'œil? (J'ai mis à jour mon contenu) – smallufo

+0

@smallufo - désolé, oublié de mentionner - placez la méthode dans une nouvelle classe. – Bozho

Questions connexes