Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Transactions | Working with Databases
Spring Boot Backend
course content

Зміст курсу

Spring Boot Backend

Spring Boot Backend

1. Backend Development Basics
2. Spring Boot Basics
3. RESTful API
4. Working with Databases
5. Testing Backend Applications

Transactions

Imagine a bank transfer operation, where money is moved from one account to another. This process involves two steps:

  1. Deducting money from one account;
  2. Depositing money into another account.

If the transaction fails after deducting money but before depositing it, the funds could be "lost." A transaction ensures that both operations are either fully completed or fully reversed.

Basics of Transaction Management

The @Transactional annotation: This is used to declare methods or classes that should be executed within the context of a transaction.

When a method with this annotation is called, Spring starts a new transaction. If the method completes successfully, the transaction is committed; otherwise, it is rolled back.

Let’s provide an example based on the real-life scenario mentioned earlier.

java

BankService

copy
1234567891011121314151617
@Service public class BankService { private AccountRepository accountRepository; @Transactional public void transferMoney(Long fromAccountId, Long toAccountId, double amount) { Account fromAccount = accountRepository.findById(fromAccountId); Account toAccount = accountRepository.findById(toAccountId); fromAccount.withdraw(amount); toAccount.deposit(amount); accountRepository.save(fromAccount); accountRepository.save(toAccount); } }

When the transferMoney method is marked with the @Transactional annotation, it means that all changes happening within this method will be executed within a single transaction.

When we call fromAccount.withdraw(amount) followed by toAccount.deposit(amount), both of these actions must be successfully completed. If, for instance, an error occurs during the toAccount.deposit(amount) operation, the transaction will automatically roll back the changes made during the fromAccount.withdraw(amount) step.

This ensures that either both operations are executed and the money is transferred, or, in the event of an error, neither operation is performed, preventing any loss of funds. The transaction guarantees that the database will never be left in an inconsistent state.

Practical Application of Transactions

//VIDEO

Available Isolation Levels

Isolation.READ_UNCOMMITTED

This level allows reading data that has not yet been committed by other transactions, which can result in "dirty reads."

Example

First, Transaction 1 updates a record but hasn’t committed the changes yet. Meanwhile, Transaction 2 comes in and reads that same record, seeing the uncommitted changes made by Transaction 1. If Transaction 1 later decides to roll back its changes, the data that Transaction 2 read becomes invalid, as it was based on incomplete, uncommitted information.

java

Main

copy
123456
@Transactional(isolation = Isolation.READ_UNCOMMITTED) public void readUncommittedData() { // May see uncommitted changes from another transaction Book book = bookRepository.findById(1L); // Here, book may contain uncommitted changes }

Isolation.READ_COMMITTED

Ensures that you only read committed data, preventing "dirty reads".

The transaction can only see data that has been committed by other transactions at the time of reading. When the transaction attempts to read data, it locks only the committed records, avoiding any "dirty reads".

Example

First, Transaction 1 updates a record and commits the changes. Meanwhile, Transaction 2 reads that same record and can only see the committed data. This ensures that Transaction 2 does not encounter any uncommitted changes made by Transaction 1, thereby preventing any "dirty reads".

java

Main

copy
123456
@Transactional(isolation = Isolation.READ_COMMITTED) public void readCommittedData() { // Reads only committed data Book book = bookRepository.findById(1L); // If the book is updated in another transaction, we won't see the changes until they are committed }

Isolation.REPEATABLE_READ

Guarantees that if you read data within a transaction, you will get the same data upon subsequent reads.

When data is first read, the transaction locks its state, preventing other transactions from making changes until it is completed. If another transaction attempts to modify this data, it will wait until the first transaction is finished, thereby eliminating any "phantom reads".

Example

Transaction 1 updates the record and commits the changes. After that, Transaction 2 attempts to retrieve the updated record. However, because Transaction 1 finished before Transaction 2 began, Transaction 2 doesn’t see the changes made by Transaction 1.

java

Main

copy
123456
@Transactional(isolation = Isolation.REPEATABLE_READ) public void repeatableReadExample() { Book book = bookRepository.findById(1L); // The book remains unchanged within this transaction // Even if another process updates this record, we won't see the changes }

Isolation.SERIALIZABLE

This is the strictest level of isolation, which prevents any conflicts between transactions but can lead to locking and decreased performance.

All transactions operate as if they were executed sequentially, one after the other. This is achieved by locking all records that the transaction reads or modifies, thereby preventing any "phantom reads" and ensuring the highest level of data integrity.

Example

Transaction 1 begins its operation and updates the record. Meanwhile, Transaction 2 tries to read the same record but has to wait for Transaction 1 to finish. This ensures that Transaction 2 doesn’t access the data until it’s safe, maintaining transaction integrity.

java

Main

copy
12345
@Transactional(isolation = Isolation.SERIALIZABLE) public void serializableExample() { Book book = bookRepository.findById(1L); // Other transactions won't be able to read or modify this record until the current transaction is complete }

Summary

Transaction isolation levels define the interaction between operations in different transactions and impact data integrity. A transaction is a logical unit of work that ensures that all operations are completed successfully or none at all, maintaining consistency in the database.

1. What is a `transaction` in the context of a database?
2. Which isolation level guarantees that you only see committed data and prevents "dirty reads"?
3. Which isolation level ensures that a transaction can see its own changes but cannot see changes made by other transactions until it is completed?

What is a transaction in the context of a database?

Виберіть правильну відповідь

Which isolation level guarantees that you only see committed data and prevents "dirty reads"?

Виберіть правильну відповідь

Which isolation level ensures that a transaction can see its own changes but cannot see changes made by other transactions until it is completed?

Виберіть правильну відповідь

Все було зрозуміло?

Секція 4. Розділ 6
We're sorry to hear that something went wrong. What happened?
some-alt