2011-04-16 2 views
8

Environnement: Printemps 3, gestion des transactions personnalisées, transactions JDBCComment appeler une méthode d'annulation personnalisée dans Spring Transaction Management?

Je viens de lire les documentations Spring sur l'utilisation du modèle de transaction pour gérer la gestion des transactions. Il semblait trop complexe donc je veux demander:

La plupart de mes transactions sont liées à JDBC, ce qui signifie que je viens de déclarer un sur mon service. Mais maintenant Je fais un appel de service REST à un autre site qui doit annuler si l'une des opérations JDBC suivantes échouent, je fournirai le code de restauration dans ce cas.

Comme je progresse dans ma méthode, dans ma transaction - Je veux enregistrer une référence à l'appel de service REST (nécessaire pour faire reculer cette action), et sur exception que je viens veulent une méthode myCustomRollback() appelé qui peut accéder à l'objet précédemment stocké. Pourquoi ne pas simplement fournir une carte dans transactionTemplate pour stocker des données et définir une méthode d'annulation personnalisée sur l'annotation @Transactional?

Voilà comment je pense à cela, je ne suis pas comme Spring le pense. Quelqu'un peut-il m'aider à combler le fossé entre ce que je veux et comment je l'accomplis le plus efficacement au printemps? Je n'ai besoin de faire cela que pour quelques opérations spéciales.

Répondre

0

vous pouvez utiliser les conseils AfterThrowing (lorsqu'une exception est levée) & appeler votre méthode (myCustmRollback()) là-bas, vous pouvez utiliser TransactionSynchronizationManager classe pour obtenir thecurrent transaction & rouleau en arrière ...

alternativement .. vous peut utiliser le AroundAdvice pour commencer & cOMMIT/rollback transaction (cette façon, vous pouvez utiliser le ressort fourni gestionnaire de transactions en utilisant la classe TransactionSynchronizationManager)

1

gestion des transactions de printemps le comportement par défaut de rollback automatique est des exceptions non vérifiées

donc une exception personnalisée,

@Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class) 
public void doSomething(... 
) 

la transaction rollback s'il y a une exception qui correspond à celle spécifiée. Si une exception non matchs, il se propage à l'appelant de l'emballage de service ou TransactionRolledBackException

si vous utilisez l'utiliser org.springframework.transaction.PlatformTransactionManager il est traitement plus faciles à gérer que des exceptions modèle

vérifier la documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html

4

J'ai relu votre question plusieurs fois et je ne suis pas sûr de comprendre complètement votre question. Je suppose que votre exécution someCode et si cela échoue, vous souhaitez exécuter myCustomRollback qui a quelques informations sur someCode. Donc, je vais essayer de fournir une réponse générique.

Si vous voulez que le ressort annule du code. Il annulera seulement ce qui est rollBackAble, comme les transactions jdbc. Supposons que vous avez une méthode qui effectue 2 appels.

@Transactional 
public void doStuff(SomeEntity entity, File file) { 
    persist(entity); 
    customFileService.createOnFileSystem(file); 
    throw new RunTimeException(); 
} 

Ainsi, le code ci-dessus sera toujours annulé. Cela annulera la persistance de votre entité, mais pas la création de votre fichier, car elle n'est pas gérée par les transactions Spring, sauf si vous fournissez une implémentation personnalisée.

Deuxièmement, Spring fournit 2 façons de travailler avec les transactions:

  • Spring AOP: un proxy est créé lors de l'exécution qui décorera votre code avec des trucs transactionnel. Si votre classe s'appelle MyClass, alors Spring créera un nom de classe MyClassProxy, qui encapsulera votre code dans le code transactionnel. AspectJ: au moment de la compilation, votre fichier .class sera ajusté et le code transactionnel sera intégré à votre méthode.

L'approche aspectJ semble plus difficile à configurer, mais n'est pas tellement et est beaucoup plus facile à utiliser. Puisque tout ce qui est annoté avec @Transactional sera incorporé (tissé) avec du code. Pour Spring AOP ce n'est pas le cas. Les appels de méthode internes transactionnels par exemple au printemps seront ignorés! Donc, aspectJ fournit une approche plus intuitive.

Retour à ce que je pense que votre question est (le code est dans 1 classe):

public void doSomeCode() { 
    Object restCall = initialize(); 
    try { 
     execute(restCall); 
    } catch (CustomException e) { 
     myCustomRollback(restCall; e); 
    } 
} 

@Transactional(rollbackFor = CustomException.class) 
private void execute(Object restCall) throws CustomException { 
    // jdbc calls.. 
     restCall = callRest(restCall); 
     throw new CustomException(); 
} 

void myCustomRollback(Object restCall, CustomException e) { 
    ... 
} 

Le code ci-dessus ne fonctionne qu'avec AspectJ! Depuis que vous faites des appels à la méthode interne, ce qui semble également privé! AOP à l'exécution ne peut pas gérer cela. Donc, ce qui se passe est que tout ce qui est rollbackAble en exécution sera annulé. Et dans doStuff vous avez des informations sur les objets qui ont été utilisés dans l'exécution, vous pouvez maintenant utiliser myCustomRollback pour restaurer vos objets REST manuellement.

Je ne sais pas si j'ai répondu à cette question correctement, mais j'espère que cela aidera quelqu'un avec un problème similaire.

solution
0

1 est d'implémenter votre propre gestionnaire transactionnel en étendant un

2 solution consiste à utiliser la solution classe

3 TransactionSynchronizationManager est d'utiliser @TransactionalEventListener si vous avez Spring 4

2

Pour Je lis toujours un problème similaire avec les événements printaniers - comme suggéré par Den Roman dans l'option 3. idée (scénario est fictif):

Chaque fois que j'effectuer des opérations extérieures qui doivent être annulées en même temps que la transaction, je publie un événement dans ma méthode @Transactional utilisant le support du printemps (org.springframework.context.ApplicationEventPublisher):

@Transactional 
public String placeOrder(Order order) { 
    String orderId = orderServiceGateway.createOrder(order); 
    applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId)); 
    workflowService.startWorkflow(orderId); 
    return orderId; 
} 

L'événement lui-même peut être n'importe quel objet - j'ai créé un POJO avec des détails sur l'entité distante à supprimer.

J'enregistré un écouteur d'événement spécial qui est lié à une phase de transaction - dans mon cas au rollback:

@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK) 
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) { 
    String orderId = orderCreatedEvent.getOrderId(); 
    orderServiceGateway.deleteOrder(orderId); 
} 

Bien sûr, il est recommandé de prendre & connecter l'exception de l'opération rollback, pas perd l'exception d'origine de la méthode placeOrder().

Par défaut, ces événements sont synchrones, mais ils peuvent être rendus asynchrones par une configuration supplémentaire.

Voici un très bon article sur ce mécanisme, y compris la configuration détaillée et les pièges: Transaction Synchronization and Spring Application Events (DZone)

Bien que je n'aime pas la solution 100% car il encombre la logique métier avec des trucs de publication d'événements et se fixe au printemps, il fait ce que je m'attends à faire et permet de passer du contexte de la méthode transactionnelle à la méthode rollback - qui n'est pas disponible via un bloc try/catch traditionnel en dehors de la méthode transactionnelle (sauf si vous mettez votre contexte dans l'exception , ce qui n'est pas très agréable).

Questions connexes