Devez-vous placer le @Transactional
dans les classes DAO
et/ou leurs méthodes ou est-il préférable d'annoter les classes de service appelant à l'aide des objets DAO? Ou est-il judicieux d’annoter les deux "couches"?
Je pense que les transactions appartiennent à la couche Service. C'est celui qui connaît les unités de travail et les cas d'utilisation. C'est la bonne réponse si vous avez plusieurs DAO injectées dans un service qui doivent fonctionner ensemble dans une seule transaction.
En général, je suis d’accord avec les autres affirmant que les transactions sont généralement lancées au niveau du service (en fonction de la granularité dont vous avez besoin bien sûr).
Cependant, dans le même temps, j'ai également commencé à ajouter @Transactional(propagation = Propagation.MANDATORY)
à ma couche DAO (et à d'autres couches non autorisées à démarrer des transactions mais nécessitant des transactions existantes) car il est beaucoup plus facile de détecter les erreurs lorsque vous avez oublié de démarrer une transaction dans l'appelant (par exemple le service). Si votre DAO est annotée avec propagation obligatoire, vous obtiendrez une exception indiquant qu'il n'y a pas de transaction active lorsque la méthode est invoquée.
J'ai également un test d'intégration dans lequel je vérifie tous les beans (post-processeur) pour cette annotation et j'échoue s'il existe une annotation @Transactional
avec une propagation autre que Obligatoire dans un bean n'appartenant pas à la couche services. De cette façon, je m'assure que nous ne commençons pas les transactions sur la mauvaise couche.
Les annotations transactionnelles doivent être placées autour de toutes les opérations qui sont inséparables.
Par exemple, votre appel est "changer le mot de passe". Cela consiste en deux opérations
Donc, dans ce qui précède, si l'audit échoue, le changement de mot de passe doit-il également échouer? Si tel est le cas, la transaction devrait se situer autour de 1 et 2 (donc au niveau de la couche service). Si l'e-mail échoue (il devrait probablement comporter une sorte d'échec sûr pour ne pas échouer), doit-il alors annuler le mot de passe de modification et l'audit?
C’est le genre de questions que vous devez vous poser pour décider où placer le @Transactional
.
Le cas normal serait d’annoter au niveau de la couche service, mais cela dépend vraiment de vos besoins.
L'annotation sur une couche de service entraînera des transactions plus longues que celles du niveau DAO. En fonction du niveau d'isolation de transaction pouvant entraîner des problèmes, les transactions simultanées ne se voyant pas modifier mutuellement, par exemple. REPEATABLE READ.
L'annotation sur les DAO gardera les transactions aussi courtes que possible, avec l'inconvénient que la fonctionnalité exposée par votre couche de service ne sera pas réalisée dans une transaction unique (rollbackable)
Il est inutile d’annoter les deux couches si le mode de propagation est défini sur défaut.
La bonne solution pour les architectures Spring traditionnelles consiste à placer une sémantique transactionnelle sur les classes de service, pour les raisons déjà décrites par d'autres.
Une tendance émergente au printemps va vers conception pilotée par le domaine } _ (DDD). Spring Roo illustre bien cette tendance. L'idée est de rendre les objets POJO du domaine beaucoup plus { plus riches que sur les architectures Spring typiques (en général, il s'agit de anemic ), et en particulier pour mettre la sémantique de transaction et de persistance sur le domaine objets eux-mêmes. Dans les cas où il suffit de simples opérations CRUD, les contrôleurs Web agissent directement sur les POJO d'objet de domaine (ils fonctionnent comme des entités dans ce contexte), et il n'y a pas de niveau de service. Dans les cas où une sorte de coordination est nécessaire entre les objets de domaine, vous pouvez avoir un bean de service qui le gère, avec @Transaction
selon la tradition. Vous pouvez définir la propagation des transactions sur les objets de domaine sur REQUIRED
pour que les objets de domaine utilisent les transactions existantes, telles que les transactions démarrées sur le bean de service.
Techniquement, cette technique utilise AspectJ et <context:spring-configured />
. Roo utilise des définitions inter-types AspectJ pour séparer la sémantique de l'entité (transactions et persistance) du contenu de l'objet de domaine (essentiellement des champs et des méthodes métier).
Je place le @Transactional
sur la couche @Service
et mets rollbackFor
toute exception et readOnly
pour optimiser la transaction davantage.
Par défaut, @Transactional
ne cherchera que RuntimeException
(exceptions non contrôlées). En rétablissant la restauration sur Exception.class
(exceptions cochées), elle annulera toute exception.
@Transactional(readOnly = false, rollbackFor = Exception.class)
Ou est-il judicieux d'annoter les deux "couches"? - Cela n'a pas de sens d'annoter à la fois la couche service et la couche dao - si l'on veut s'assurer que la méthode DAO est toujours appelée (propagée) à partir d'une couche service avec propagation "obligatoire" dans DAO. Cela fournirait certaines restrictions pour que les méthodes DAO puissent être appelées à partir de la couche d'interface utilisateur (ou des contrôleurs). En outre, lorsque vous testez la couche DAO en particulier, le fait d’avoir annoté DAO garantira également que la fonctionnalité transactionnelle est testée.
De plus, Spring recommande d'utiliser uniquement l'annotation sur des classes concrètes et non sur des interfaces.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Pour transaction au niveau base de données
la plupart du temps, j'ai utilisé @Transactional
dans DAO uniquement au niveau de la méthode; la configuration peut donc être spécifique à une méthode/à l'aide de la valeur par défaut (obligatoire)
La méthode de DAO qui récupère les données (select ..) - pas besoin de @Transactional
, ce qui peut entraîner une surcharge en raison de Transaction interceptor/et du proxy AOP qui doivent être exécutés comme Bien.
Les méthodes de DAO qui insèrent/mettent à jour obtiendront @Transactional
très bon blog sur transctional
Pour le niveau d'application -
J'utilise la logique transactionnelle pour la logique métier Je souhaite pouvoir revenir en arrière en cas d'erreur inattendue
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Habituellement, il convient de placer une transaction au niveau service.
Mais comme indiqué précédemment, l'atomicité d'une opération est ce qui nous indique où une annotation est nécessaire. Ainsi, si vous utilisez des frameworks comme Hibernate, où une seule opération "save/update/delete/... modification" sur un objet a le potentiel de modifier plusieurs lignes dans plusieurs tables Bien sûr, il devrait également y avoir une gestion des transactions sur cette méthode DAO spécifique.
@Transactional
Les annotations doivent être placées autour de toutes les opérations qui sont inséparables . La propagation de la transaction @Transactional
est gérée automatiquement. Dans ce cas, si une autre méthode est appelée par la méthode actuelle, cette méthode aura l'option de joindre la transaction en cours.
Alors prenons exemple:
Nous avons 2 modèles, à savoir Country
et City
. La cartographie relationnelle des modèles Country
et City
est comme une Country
peut avoir plusieurs villes, donc la cartographie est comme,
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
Ici, le pays est mappé sur plusieurs villes et récupérées Lazily
. Voici donc le rôle de @Transactinal
lorsque nous récupérons l’objet Pays de la base de données, puis nous obtiendrons toutes les données de l’objet Pays mais nous n’obtiendrons pas un ensemble de villes, car nous recherchons des villes LAZILY
.
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
Lorsque nous voulons accéder à l'objet Set of Cities from country, nous obtiendrons des valeurs NULL dans cet ensemble, car l'objet de l'ensemble créé uniquement, cet ensemble n'est pas initialisé avec les données pour obtenir les valeurs de l'ensemble que nous utilisons @Transactional
i.e.
//with @Transactional
@Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
Object object = country.getCities().size();
}
Donc, fondamentalement, @Transactional
is Service peut effectuer plusieurs appels en une seule transaction sans fermer la connexion avec le point final.
Il vaut mieux l'avoir dans la couche service! Ceci est clairement expliqué dans l'un des articles que j'ai rencontrés hier! Voici le lien que vous pouvez vérifier!
Tout d’abord, définissons où nous devons utiliser transaction ?
Je pense que la réponse correcte est la suivante: lorsque nous devons nous assurer que la séquence d'actions sera terminée en tant qu'une opération atomique ou qu'aucune modification ne sera apportée même si l'une des actions échoue.
Il est de notoriété publique d'intégrer la logique métier aux services. Les méthodes de service peuvent donc contenir différentes actions qui doivent être effectuées comme une seule unité logique de travail. Si tel est le cas, cette méthode doit être marquée comme Transactionnelle . Bien sûr, toutes les méthodes ne nécessitent pas une telle limitation, vous n'avez donc pas besoin de marquer un service entier comme transactionnel.
Et plus encore - n'oubliez pas de prendre en compte que @Transactional peut évidemment réduire les performances de la méthode. Pour avoir une vue d'ensemble, vous devez connaître les niveaux d'isolation des transactions. Sachant cela, vous éviterez probablement d'utiliser @Transactional là où ce n'est pas nécessairement nécessaire.
La couche de service est le meilleur endroit pour ajouter des annotations @Transactional
car la plupart de la logique métier présente ici, elle contient un comportement de cas d'utilisation de niveau de détail.
Supposons que nous l’ajoutions à DAO et que, du service, nous appelions 2 classes DAO, une en échec et un autre succès. Dans ce cas, si @Transactional
ne figure pas dans le service, une base de données sera validée et une autre annulée.
Par conséquent, ma recommandation est d'utiliser judicieusement cette annotation et d'utiliser uniquement la couche Service.
Idéalement, la couche de service (gestionnaire) représente votre logique d’entreprise. Elle doit donc être annotée avec @Transactional
. La couche de service peut appeler différents DAO pour effectuer des opérations de base de données. Supposons des situations où vous avez N nombre d'opérations DAO dans une méthode de service. Si votre première opération DAO a échoué, d'autres risquent de passer et vous obtiendrez un état de base de données incohérent. L'annotation de la couche Service peut vous éviter de telles situations.
Le @Transactional
doit être utilisé sur la couche de service car il contient la logique applicative. La couche DAO ne comporte généralement que des opérations CRUD de base de données.
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
Printemps doc: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
Je préfère utiliser @Transactional
sur la couche services au niveau de la méthode.
@Transactional
utilise dans la couche de service appelée à l'aide de la couche contrôleur (@Controller
) et de l'appel de la couche de service à la couche DAO (@Repository
), à savoir, une opération liée à la base de données.
Il est préférable de conserver @Transactional dans une couche intermédiaire distincte entre DAO et Service Layer . Étant donné que la restauration est très importante, vous pouvez placer toute votre manipulation de base de données dans la couche intermédiaire et écrire la logique métier dans Service Layer. La couche intermédiaire interagira avec vos couches DAO.
Cela vous aidera dans de nombreuses situations, telles que ObjectOptimisticLockingFailureException - Cette exception se produit uniquement après la fin de votre transaction. Donc, vous ne pouvez pas l'attraper dans la couche intermédiaire, mais vous pouvez l'attraper dans votre couche de service maintenant. Cela ne serait pas possible si vous avez @Transactional dans la couche Service. Bien que vous puissiez attraper dans Controller, mais Controller doit être aussi propre que possible.
Si vous envoyez des messages ou des SMS dans un fil séparé après avoir terminé toutes les options de sauvegarde, de suppression et de mise à jour, vous pouvez le faire dans le service une fois la transaction terminée dans la couche intermédiaire. Encore une fois, si vous mentionnez @Transactional dans la couche service, votre courrier ira même si votre transaction échoue.
Donc, avoir une couche intermédiaire @Transaction aidera à rendre votre code meilleur et facile à manipuler. Autrement, Si vous utilisez la couche DAO, vous ne pourrez peut-être pas annuler toutes les opérations . Si vous utilisez la couche Service, vous devrez peut-être utiliserAOP(Programmation orientée aspect) dans certains cas.