Spring provides a wide range of transaction management mechanisms:
- DataSourceTransactionManager: Manages transactions directly at the JDBC level, typically used with
JdbcTemplate
or plain JDBC.
- HibernateTransactionManager: Integrates Hibernate transactions with Spring, working directly with Hibernate’s
SessionFactory
.
- JpaTransactionManager: Used for managing transactions in a JPA context, integrating Spring with JPA’s
EntityManager
.
- Configures Hibernate's
SessionFactory
in Spring applications. Transactions were typically managed using the@Transactional
annotation.
- JtaTransactionManager: Enables global (distributed) transactions across multiple resources (databases, JMS, etc.) using Java Transaction API (JTA). Typically used in enterprise applications that need multi-resource transactions.
- ReactiveTransactionManager: Provides transaction management in a non-blocking, reactive programming environment, mainly used with R2DBC (Reactive Relational Database Connectivity).
- ChainedTransactionManager: Coordinates transactions across multiple resources (e.g., JDBC, JMS) by chaining multiple transaction managers.
- JmsTransactionManager: Handles transactions for Java Message Service (JMS), ensuring messaging operations are part of a transactional workflow.
- MongoTransactionManager: Manages transactions in MongoDB, primarily for multi-document transactions.
- Declarative Transaction Management (@Transactional): Automatically defines transaction boundaries without manual handling of transaction logic.
- Programmatic Transaction Management: Offers explicit control over transactions using
TransactionTemplate
or directly managing transactions throughPlatformTransactionManager
.
- A method is called on a transactional bean.
- The proxy checks for an existing transaction.
- A new transaction is started (if necessary).
- The method is executed.
- If no exception occurs, the transaction is committed; if an exception occurs, it is rolled back.
- Resources are cleaned up.
When you annotate a class or method with @Transactional
, Spring creates a proxy around your bean. There are two types of proxies:
- JDK Dynamic Proxies: Created if the class implements one or more interfaces.
- CGLIB Proxies: Created if the class does not implement interfaces or is configured explicitly.
- Transaction context is checked when a method annotated with
@Transactional
is called. - Based on propagation settings, the proxy either joins an existing transaction or creates a new one.
- The proxy interacts with the transaction manager to start a transaction, acquire resources, and manage the lifecycle of the transaction.
- The proxy executes the actual method logic within the transactional context.
- If an exception occurs, the transaction is rolled back based on rollback rules (typically for unchecked exceptions).
- If the method completes successfully, the transaction is committed.
@Transactional
offers fine-grained control over transaction behavior, including:
- REQUIRED: Joins an existing transaction or creates a new one.
- REQUIRES_NEW: Suspends the current transaction and starts a new one.
- SUPPORTS, MANDATORY, NEVER, NOT_SUPPORTED, NESTED: Other propagation settings to define transaction behavior.
Defines visibility of changes across transactions:
- READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE.
Sets the maximum duration for a transaction:
@Transactional(timeout = 30)
Optimizes performance for read-only transactions:
@Transactional(readOnly = true)
Specifies which exceptions trigger a rollback:
@Transactional(rollbackFor = Exception.class)
Synchronizes transactional resources and ensures proper commit or rollback.
Allows creating savepoints within transactions using NESTED
propagation.
Specifies different transaction managers for different resources.
When you annotate a class or method with @Transactional
:
- Bean Registration: Spring detects the
@Transactional
annotation. - Proxy Generation: Spring creates a proxy (either JDK Dynamic Proxy or CGLIB Proxy).
- Method Interception: The proxy intercepts calls to the bean's methods and manages transaction behavior.
When a method on the proxy is invoked: Advice Execution: The proxy executes any associated advice (in this case, transaction management logic) before and/or after the actual method invocation. This is where it checks transaction settings like propagation and isolation.
- Incoming method call is intercepted.
- Proxy checks for an existing transaction.
- Based on propagation, either joins or starts a new transaction.
- Executes the target method.
- Handles exceptions and performs rollback if necessary.
- Cleans up resources after method execution.
@PersistenceContext
is used to inject an EntityManager
in the data access layer when working with JPA.
Injects EntityManager
automatically into your repositories or service classes.
- The
EntityManager
is bound to the current transaction. - Automatic flushing of changes before transaction commits.
- unitName: Specifies the persistence unit if multiple are defined.
- type: Can be
TRANSACTION
orEXTENDED
for managing different scopes of theEntityManager
.
In the context of JPA (Java Persistence API) and Hibernate, flushing refers to the process of synchronizing the in-memory state of the persistence context (also known as the first-level cache) with the database. Specifically, it involves sending the changes made to managed entities (in memory) to the database for execution (such as INSERT
, UPDATE
, and DELETE
statements).
Here’s a detailed explanation of how flushing works and its significance:
- In JPA, when you perform operations on entities (like persisting, updating, or removing), these changes are not immediately reflected in the database.
- Instead, changes are made to the in-memory representation of these entities in the persistence context (managed by an
EntityManager
in JPA orSession
in Hibernate). - These changes reside in this context and are only propagated to the database when flushing occurs.
- Flushing is the process by which the changes in the persistence context are written to the database (but not necessarily committed). This ensures that the database state matches the in-memory state.
- It includes generating SQL statements for the changes and executing them against the database.
Flushing can occur automatically or manually in JPA/Hibernate, depending on the specific conditions. Here are common scenarios:
-
Before Query Execution:
- If there are pending changes (unflushed changes) in the persistence context and a query is about to be executed, JPA/Hibernate will automatically flush to ensure the query results reflect the most up-to-date data.
-
Transaction Commit:
- When a transaction is committed, all pending changes in the persistence context are flushed to the database automatically. After the flush, the transaction is committed (finalized in the database).
-
EntityManager/Session Close:
- When an
EntityManager
orSession
is closed, it triggers a flush if there are unsaved changes to ensure data consistency.
- When an
-
You can manually trigger a flush in JPA by calling:
entityManager.flush();
- This forces the synchronization of in-memory changes with the database.
-
In Hibernate, similarly, you can use:
session.flush();
- Flushing is not the same as committing a transaction. When a flush occurs, changes are sent to the database, but the transaction is still active and can be rolled back if necessary.
- Commit is the final step, where all flushed changes are made permanent in the database.
Hibernate provides different flush modes to control when flushing occurs. These can be configured via FlushMode
:
6.5.1. FlushMode.AUTO
(default):
- Flushes automatically before query execution or before transaction commit. This is the default behavior.
6.5.2. FlushMode.COMMIT
:
- Flushes only at the time of the transaction commit. No intermediate flushes before query execution.
6.5.3. FlushMode.MANUAL
:
- No automatic flushing occurs. Flushing must be done manually by explicitly calling
flush()
.
6.5.4. FlushMode.ALWAYS
:
- Flushes the persistence context after every modification operation, ensuring that the database is always up to date, but this can negatively affect performance.
Flushing helps maintain data consistency between the application’s in-memory state and the actual database state. It is especially important in scenarios where:
- You need to run queries that depend on the latest changes made to entities.
- You want to ensure that changes to the database occur in the correct order.
- The application requires precise control over the moment when changes are propagated to the database.
Flushing can be a costly operation because it generates and executes SQL statements. If done too frequently, it can negatively impact performance, particularly in high-transaction systems. For this reason, Hibernate provides flush modes to optimize when flushing should occur.
Consider a scenario where you have a Hibernate session:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
EntityA entityA = new EntityA();
entityA.setValue("Initial Value");
// The entity is persisted, but not yet flushed to the database
session.save(entityA);
// Changes are still in-memory, not in the database
entityA.setValue("Updated Value");
// Flushing manually forces the update to be sent to the database
session.flush();
// Transaction commit makes the change permanent
tx.commit();
session.close();
In the above example, flushing occurs manually using session.flush()
, ensuring that the update is reflected in the database before the transaction is committed.
- Flushing is the process of synchronizing the in-memory state of managed entities with the database.
- It ensures that changes are sent to the database, though those changes are not final until the transaction is committed.
- Flushing can occur automatically or manually, depending on the configuration and behavior of your application.
- Proper management of flushing is crucial for ensuring both data consistency and performance in JPA and Hibernate applications.