--- Hibernate/Spring Transaction concepts (ultra) importants à connaître (classés par nbr ***), extraits des documents suivants: http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/ http://docs.jboss.org/hibernate/stable/core.old/reference/fr/html/transactions.html (doc un peu vieille mais partiellement en français) http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch02.html récent (hibernate 4.1) http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html (La référence officielle coté Spring pour la gestion des transactions) 1) Gestion de session et délimitation de transactions **) SessionFactory: single instance ----------------------------------- SessionFactory est un objet complexe et optimisé pour fonctionner avec les threads(thread- safe). Il est coûteux à créer et est ainsi prévu pour n'être instancié qu'une seule fois via un objet Configuration au démarrage de l'application, et être partagé par tous les threads d'une application. **) Session is lightweight and "one-shot" used ---------------------------------------------- Un objet Session est relativement simple et n'est threadsafe. Il est également peu coûteux à créer. Il devrait n'être utilisé qu'une seule fois, pour un processus d'affaire ou une unité de travail ou une conversation et ensuite être relâché. **) transaction: one term definition for various level/scope usage ------------------------------------------------------------------- This documentation largely treats the physical and logic notions of transaction as one-in-the-same. (physical transaction with the database, logical notion of a transaction as related to a persistence context, application notion of a Unit-of-Work, as defined by the archetypal pattern) **) Transaction must be short timed ----------------------------------- Une transaction de base de données se doit d'être la plus courte possible afin de réduire les risques de collision sur des enregistrements verrouillés. De longues transactions à la base de données nuiront à l'extensibilité de vos applications lorsque confrontées à de hauts niveaux de charge. Par conséquent, il n'est jamais bon de maintenir une transaction ouverte pendant la durée de reflexion de l'utilisateur, jusqu'a ce que l'unité de travail soit achevée. ***) Transactions are mandatory (by definition) ------------------------------------------------ Les transactions avec la base de données ne sont JAMAIS optionnelles, toute communication avec une base de données doit se dérouler dans une transaction, peu importe si vous lisez ou écrivez des données. (Database transactions are NEVER optional. All communication with a database must be encapsulated by a transaction.) (No communication with the database can occur OUTSIDE of a database transaction) (Avoid auto-commit behavior for reading data, because many small transactions are unlikely to perform better than one clearly-defined unit of work, and are more difficult to maintain and extend. ) **) Transaction support ----------------------- In the world of Java there are 2 well defined mechanism for dealing with transactions in JDBC: JDBC itself and JTA. Hibernate supports both mechanisms for integrating with transactions and allowing applications to manage physical transactions. org.hibernate.engine.transaction.spi.TransactionFactory interface which serves 2 main functions - JTA environment, Physical transaction state, ... - Factory for org.hibernate.Transaction (logical transaction) instances which are used to allow applications to manage and check the state of transactions. (JPA has a similar notion in the javax.persistence.EntityTransaction interface) Physical Transactions support: - JDBC (java.sql.Connection.commit() and java.sql.Connection.rollback(), with org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory class) - JTA (javax.transaction.UserTransaction obtained from org.hibernate.service.jta.platform.spi.JtaPlatform, represented by the org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory) - CMT (javax.transaction.TransactionManager, obtained from org.hibernate.service.jta.platform.spi.JtaPlatform, represented by the org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory) - Possibility to plug in a custom transaction approach by implementing the org.hibernate.engine.transaction.spi.TransactionFactory contract **) Transaction Lock Paradigm ----------------------------- The Hibernate org.hibernate.Session acts as a transaction-scoped cache providing repeatable reads for lookup by identifier and queries that result in loading entities. Important: To reduce lock contention in the database, the physical database transaction needs to be as short as possible. Long database transactions prevent your application from scaling to a highly-concurrent load. Do not hold a database transaction open during end-user-level work, but open it after the end-user-level work is finished. This is concept is referred to as transactional write-behind. 1.1) Unité de travail ***) Many Operation to One Session --------------------------------- utiliser un paradigme session-par-operation est un anti-pattern. N'ouvrez et ne fermez pas la Session à chacun de vos accès simples à la base de données dans un même thread. (l'utilisation d'une connexion auto-commit constitue le même anti-pattern. Ce mode de fonctionnement existe pour les applications émettant des commandes SQL à partir d'une console. Hibernate désengage le mode auto-commit et s'attend à ce qu'un serveur d'applications le fasse également.) ***) Transactions: aggregate of operation ----------------------------------------- plusieurs petites transactions ne seront jamais aussi efficaces qu'une seule plus grosse clairement définie comme unité de travail. Ce dernier choix et en plus beaucoup plus facile a maintenir et à faire évoluer. This is an anti-pattern of opening and closing a Session for each database call in a single thread. It is also an anti-pattern in terms of database transactions. Group your database calls into a planned sequence, do not auto-commit after every SQL statement in your application. ******) Session-per-request pattern + One Session to One Transaction per (HTTP)Request -------------------------------------------------------------------------------------- The most common transaction pattern in a multi-user client/server application is session-per-request. In this model, a request from the client is sent to the server (where the Hibernate persistence layer runs), At the beginning of handling such a request, a new Hibernate Session is opened, starts a transaction, performs all data related work, Once the work has been completed (and the response for the client has been prepared) ends the transaction, closes and flushes the Session; and all database operations are executed in this unit of work. The crux of the pattern is the one-to-one relationship between the transaction and the Session. You would also use a single database transaction to serve the clients request, starting and committing it when you open and close the Session. The relationship between the two is one-to-one and this model is a perfect fit for many applications. Hibernate provides built-in management of the "current session" to simplify this pattern. All you have to do is start a transaction when a server request has to be processed, and end the transaction before the response is sent to the client. You can do this in any way you like, common solutions are ServletFilter, AOP interceptor with a pointcut on the service methods, or a proxy/interception container (An EJB container is a standardized way to implement cross-cutting aspects such as transaction demarcation on EJB session beans, declaratively with CMT) -> Spring @nnotation declaration !!! ------------------------------------ @Transactional @nnotation Best Practice ****) transactions belong on the Service layer It's the one that knows about units of work and use cases. It's the right answer if you have several DAOs injected into a Service that need to work together in a single transaction ****) @nnotation Policy The normal case would be to annotate on a service layer level, but this REALLY DEPENDS ON YOUR REQUIREMENTS. Annotating on a service layer will result in longer transactions than annotating on DAO level. Annotating on the DAOs will keep the transactions as short as possible, with the drawback that the functionality your service layer is exposing wont be done in a single (rollbackable) transaction. It does not make sense to annotate both layers if the propagation mode is set to default. ------------------------------------ Spring doc: - Comprehensive transaction support is among the most compelling reasons to use the Spring Framework - Consistent programming model across different transaction APIs such as Java Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA), and Java Data Objects (JDO) Global transactions enable you to work with multiple transactional resources, typically relational databases and message queues. The application server manages global transactions through the JTA, which is a cumbersome API to use (partly due to its exception model). Furthermore, a JTA UserTransaction normally needs to be sourced from JNDI Previously, the preferred way to use global transactions was via EJB CMT (a form of declarative transaction management, distinguished from programmatic transaction management) The significant downside is that CMT is tied to JTA and an application server environment Local transactions are resource-specific, such as a transaction associated with a JDBC connection Local transactions may be easier to use, but have significant disadvantages: they cannot work across multiple transactional resources code that manages transactions using a JDBC connection cannot run within a global JTA transaction. Because the application server is not involved in transaction management, it cannot help ensure correctness across multiple resources ****) Most applications use a single transaction resource. Another downside is that local transactions are invasive to the programming model. Spring Framework's consistent programming model (in any environment) -> benefit from different transaction management strategies in different environments. -> transaction management: both * declarative -> prefered & recommended, little or no code related to transaction management, -> least impact on application code, most consistent with the ideals of a non-invasive lightweight container. -> made possible with Spring aspect-oriented programming (AOP) proxies: transactional advice is driven by metadata (XML or annotation-based) -> Yield of an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations. -> Specify transaction behavior (or lack of it) down to individual method level. -> setRollbackOnly() call within a transaction context if necessary -> follows EJB convention (roll back is automatic only on unchecked/runtime exceptions) -> Unlike EJB CMT, tied to JTA, it works (file-configured) in any environment, with JTA transactions or local transactions using JDBC, JPA, Hibernate or JDO. -> applicable to any class, not merely special classes such as EJBs. -> declarative rollback rules feature(insert custom behavior in the case of transaction rollback, add arbitrary advice, along with the transactional advice, -> specify which exceptions/throwables should cause automatic rollback: business objects do not depend on the transaction infrastructure (no need to import Spring transaction/other APIs). -> customize transactional behavior, by using AOP (insert custom behavior in case of transaction rollback, add arbitrary advice, along with the transactional advice) -> does not support propagation of transaction contexts across remote calls, as do high-end application servers. If you need this feature, use EJB... -> In addition to the XML-based declarative approach to transaction configuration, you can use an annotation-based approach (declarations are much closer to the affected code) -> @Transactional annotation placed (to activate transactional behavior) on interface/class definition, interface's / (class public) method, is simply metadata that can be consumed by @Transactional-aware runtime infrastructure, to configure beans with transactional behavior ( element switches on the transactional behavior). -> ([for compliance/scaling] Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Transactional annotation, as opposed to annotating interfaces) -> [@Transactional annotation has additionnal Exceptions rollbackFor/noRollbackFor attributes, for custom settings] -> Spring's meta-annotation support allows you to define custom shortcut annotations for your [repeated] specific use cases [(ex: @OrderTx, @AccountTx, coming from @interface user base definition)] -> [Spring Transaction propagation settings/options:] -> PROPAGATION_REQUIRED: a logical transaction scope is created for each method, [...] all these scopes will be mapped to the same physical transaction -> PROPAGATION_REQUIRES_NEW: uses a completely independent transaction for each affected transaction scope, underlying physical transactions are different and hence can commit or roll back independently, (transaction not affected by an inner transaction's rollback status) -> PROPAGATION_NESTED: single physical transaction with multiple savepoints that it can roll back to. Partial rollbacks allow an inner transaction scope to trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back * programmatic -> more optionnal, but very powerfull: Spring Framework transaction abstraction can run over any underlying transaction infrastructure. -> Programmatic rollback is available should you absolutely need it, but its usage flies in the face of achieving a clean POJO-based architecture. !!! you do not need an application server simply for declarative transactions through EJBs. In fact, even if your application server has powerful JTA capabilities, you may decide that the Spring Framework's declarative transactions offer more power and a more productive programming model than EJB CMT. (you need an application server's JTA capability only if your application needs to handle transactions across multiple resources) Transaction strategy: The key to the Spring transaction abstraction, defined by the org.springframework.transaction.PlatformTransactionManager interface (TransactionStatus getTransaction(TransactionDefinition definition), commit/rollback(TransactionStatus status)) -> can be used programmatically from your application code -> mocked or stubbed as necessary -> not tied to a lookup strategy such as JNDI -> can be tested much more easily PlatformTransactionManager implementations normally require knowledge of the environment in which they work: JDBC, JTA, Hibernate, and so on Spring abstraction (JDBC support, JPA support or Hibernate support) > use of direct APIs / DataSourceUtils / other helper classes, 1.2) Longue conversation **) Client side implementation responsibility --------------------------------------------- This is an anti-pattern to have Session and database transaction open while the user is editing (using database-level locks to prevent other users from modifying the same data and to guarantee isolation and atomicity). Instead, we have to use several database transactions to implement the conversation. In this case, maintaining isolation of business processes becomes the PARTIAL RESPONSABILITY OF THE APPLICATION TIER. ****) session-per-request-with-detached-objects ----------------------------------------------- Si vous décidez d'utiliser le paradigme session-par-requête discuté plus haut, toutes les entités chargées en mémoire deviendront des objets détachés durant le temps de réflexion de l'usager. Hibernate vous permet de rattacher ces objets et de persister les modifications y ayant été apportées. ***) Extended (or Long) Session Pattern --------------------------------------- The Hibernate Session may be disconnected from the underlying JDBC connection after the database transaction has been committed, and reconnected when a new client request occurs. This pattern is known as session-per-conversation and makes even reattachment unnecessary. Automatic versioning is used to isolate concurrent modifications and the Session is usually not allowed to be flushed automatically, but explicitly. 1.3) L'identité des objets ****) Instance Equality: JVM vs BD policies ------------------------------------------- Une application peut accéder à la même entité persistante de manière concurrente dans deux Sessions différentes. Toutefois, une instance d'une classe persistante n'est jamais partagée par deux instances distinctes de la classe Session. Il existe donc deux notions de l'identité d'un objet: Identité BD: foo.getId().equals( bar.getId() ) Identité JVM: foo==bar Then for objects attached to a particular Session (i.e. in the scope of a Session) the two notions are equivalent, and JVM identity for database identity is guaranteed by Hibernate. However, while the application might concurrently access the "same" (persistent identity) business object in two different sessions, the two instances will actually be "different" (JVM identity). Le développeur doit donc redéfinir l'implémentation des méthodes equals() et hashcode() dans les classes persistantes et y adjoindre sa propre notion d'identité. Il existe toutefois une restriction: Il ne faut jamais utiliser uniquement l'identifiant de la base de données dans l'implémentation de l'égalité; Il faut utiliser une clé d'affaire, généralement une combinaison de plusieurs attributs uniques, si possible immuables. 1.4) Problèmes communs il est recommandé de ne jamais utiliser les anti-patterns session-per-user-session et session-per-application.