Spring Boot and Spring Data JPA make the handling
of transactions extremely simple. They enable you to declare your preferred
transaction handling and provide seamless integration with Hibernate and JPA. The only thing you need to do is to annotate one of your methods with @Transactional. But what does that actually do? Which method(s) should you annotate with @Transactional? And why can you set different propagation
levels? I will answer all of these questions in this video. To make that a little bit easier to understand, I will focus on local transactions. These are transactions between your application and 1 external system, for example your database. Hi, I’m Thorben Janssen. If you are new here and you want to learn
how to create your entity mappings with ease, build incredible efficient persistence layers with Hibernate and Spring and all types of other Java persistence related stuff, start now by subscribing and clicking the bell, so you don't miss anything. And if you have any relevant topic that you
want me to cover on this channel and on my blog, please let me know in the comments below,
I will try my best to cover those. From a Spring application point of view, the same concepts also apply to distributed transactions. So, if you’re using distributed transactions, I recommend that you keep reading and research the required configuration parameters for distributed transactions afterward. OK, before we can talk about Spring’s transaction
support, we need to take a step back and explain database transactions in general and take a quick look at JDBC’s transaction management. This is necessary because Spring’s transaction management is based on the transaction management provided by your database and the JDBC specification. Transactions manage the changes that you perform in one or more systems. These can be databases, message brokers, or any other kind of software system. The main goal of a transaction is to provide
ACID characteristics to ensure the consistency and validity of your data. ACID is an acronym that stands for atomicity, consistency, isolation, and durability: Atomicity describes an all or nothing principle. Either all operations performed within the transaction get executed or none of them. That means if you commit the transaction successfully, you can be sure that all operations got performed. It also enables you to abort a transaction and roll back all operations if an error occurs. The consistency characteristic ensures that your transaction takes a system from one consistent state to another consistent state. That means that either all operations were rolled back and the data was set back to the state you started with or the changed data passed all consistency checks. In a relational database, that means that
the modified data needs to pass all constraint checks, like foreign key or unique constraints, defined in your database. Isolation means that changes that you perform within a transaction are not visible to any other transactions until you commit them successfully. Durability ensures that your committed changes get persisted. As you can see, a transaction that ensures these characteristics makes it very easy to keep your data valid and consistent. Relational databases support ACID transactions, and the JDBC specification enables you to control them. Spring provides annotations and different
transaction manager to integrate transaction management into their platform and to make it easier to use. But in the end, it all boils down to the features
provided by these lower-level APIs. There are 3 main operations you can do via
the java.sql.Connection interface to control an ACID transaction on your database. You can: Start a transaction by getting a Connection and deactivating auto-commit. This gives you control over the database transaction. Otherwise, you would automatically execute
each SQL statement within a separate transaction. Commit a transaction by calling the commit()
method on the Connection interface. This tells your database to perform all required consistency checks and persist the changes permanently. Rollback all operations performed during the
transaction by calling the rollback() method on the Connection interface. You usually perform this operation if an SQL statement failed or if you detected an error in your business logic. As you can see, conceptually, controlling a database transaction isn’t too complex. But implementing these operations consistently
in a huge application, Is a lot harder than it might seem. That’s where Spring’s transaction management comes into play. Spring provides all the boilerplate code that’s required to start, commit, or rollback a transaction. It also integrates with Hibernate’s and JPA’s transaction handling. If you’re using Spring Boot, this reduces
your effort to a @Transactional annotation on each interface, method, or class that shall be executed within a transactional context. If you’re using Spring without Spring Boot, you need to activate the transaction management by annotating your application class with @EnableTransactionManagement. Here you can see a simple example of a service
with a transactional method. The @Transactional annotation tells Spring
that a transaction is required to execute this method. When you inject the AuthorService somewhere, Spring generates a proxy object that wraps the AuthorService object and provides the required code to manage the transaction. By default, that proxy starts a transaction
before your request enters the first method that’s annotated with @Transactional. After that method got executed,
the proxy either commits the transaction or rolls it back if a RuntimeException or Error occurred. Everything that happens in between, including all method calls, gets executed within the context of that transaction. The @Transactional annotation supports a set
of attributes that you can use to customize the behavior. The most important ones are propagation, readOnly, rollbackFor, and noRollbackFor. Let’s take a closer look at each of them. Spring’s Propagation enum defines 7 values that you can provide to the propagation attribute of the @Transactional annotation. They enable you to control the handling of existing and creation of new transactions. You can choose between: REQUIRED to tell Spring to either join an
active transaction or to start a new one if the method gets called without a transaction. This is the default behavior. SUPPORTS to join an activate transaction if one exists. If the method gets called without an active transaction, this method will be executed without a transactional context. MANDATORY to join an activate transaction if one exists or to throw an Exception if the method gets called without an active transaction. NEVER to throw an Exception if the method
gets called in the context of an active transaction. NOT_SUPPORTED to suspend an active transaction and to execute the method without any transactional context. REQUIRES_NEW to always start a new transaction for this method. If the method gets called with an active transaction, that transaction gets suspended until this method got executed. NESTED to start a new transaction if the method gets called without an active transaction. If it gets called with an active transaction,
Spring sets a savepoint and rolls back to that savepoint if an Exception occurs. If you want to implement a read-only operation, I recommend using a DTO projection. It enables you to only read the data you actually need for your business code and provides a much better performance. But if you decide to use an entity projection anyways, you should at least mark your transaction as read-only. Since Spring 5.1, this sets Hibernate’s query hint org.hibernate.readOnly and avoids dirty checks on all retrieved entities. I explained earlier, that the Spring proxy automatically rolls back your transaction if a RuntimeException or Error occurred. You can customize that behavior using the rollbackFor and noRollbackFor attributes of the @Transactional annotation. As you might guess from its name, the rollbackFor attribute enables you to provide an array of Exception classes for which the transaction shall be rolled back. And the noRollbackFor attribute accepts an
array of Exception classes that shall not cause a rollback of the transaction. In this example, I want to roll back
the transaction for all subclasses of the Exception class except the EntityNotFoundException. Spring Boot and Spring Data JPA provide an easy to use transaction handling. You only need to annotate your interface, class, or method with Spring’s @Transactional annotation. Spring then wraps your service in a generated
proxy that joins an active transaction or starts a new one and commits or rolls the transaction back after your method got executed. You can customize the default behavior using the propagation, readOnly, rollbackFor, and noRollbackFor attributes: The propagation attribute gives you control
over the handling of existing and the creation of new transactions. If your method gets called within the context of an activate transaction, you can, for example, decide if your method shall join that transaction, create a new one, or fail. You can use the readOnly attribute to improve the performance of read-only operations. The rollbackFor and noRollbackFor attributes enable you to define which Exception classes will cause a rollback of your transaction and which can be handled by your business logic. Scalar projections are returned as Object arrays or instances of the Tuple interface. Both versions don’t provide any type-information and are hard to use. Even though they are very efficient for read-only operations, you should avoid them in your application. OK, that’s it for today. If you want to learn more about Hibernate, you should join the free Thoughts on Java Library. It gives you free access to a lot of member-only
content like a cheat for this video and an ebook about using native queries with JPA
and Hibernate. I’ll add the link to it to the video description below. And if you like today’s video, please give it a thumbs up and subscribe below. Bye