Browse Source

Document that @Transactional does not propagate to new threads

Closes gh-25439
pull/26273/head
Juergen Hoeller 5 years ago
parent
commit
322babc04a
  1. 19
      spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
  2. 4
      spring-tx/src/main/java/org/springframework/transaction/interceptor/RuleBasedTransactionAttribute.java
  3. 117
      src/docs/asciidoc/data-access.adoc

19
spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java

@ -38,16 +38,25 @@ import org.springframework.transaction.TransactionDefinition; @@ -38,16 +38,25 @@ import org.springframework.transaction.TransactionDefinition;
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
* class, and in fact {@link AnnotationTransactionAttributeSource} will directly
* convert the data to the latter class, so that Spring's transaction support code
* does not have to know about annotations. If no rules are relevant to the exception,
* it will be treated like
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
* (rolling back on {@link RuntimeException} and {@link Error} but not on checked
* exceptions).
* does not have to know about annotations. If no custom rollback rules apply,
* the transaction will roll back on {@link RuntimeException} and {@link Error}
* but not on checked exceptions.
*
* <p>For specific information about the semantics of this annotation's attributes,
* consult the {@link org.springframework.transaction.TransactionDefinition} and
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
*
* <p>This annotation commonly works with thread-bound transactions managed by
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
* transaction to all data access operations within the current execution thread.
* <b>Note: This does NOT propagate to newly started threads within the method.</b>
*
* <p>Alternatively, this annotation may demarcate a reactive transaction managed
* by {@link org.springframework.transaction.ReactiveTransactionManager} which
* uses the Reactor context instead of thread-local attributes. As a consequence,
* all participating data access operations need to execute within the same
* Reactor context in the same reactive pipeline.
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @author Sam Brannen

4
spring-tx/src/main/java/org/springframework/transaction/interceptor/RuleBasedTransactionAttribute.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,7 +29,7 @@ import org.springframework.lang.Nullable; @@ -29,7 +29,7 @@ import org.springframework.lang.Nullable;
/**
* TransactionAttribute implementation that works out whether a given exception
* should cause transaction rollback by applying a number of rollback rules,
* both positive and negative. If no rules are relevant to the exception, it
* both positive and negative. If no custom rollback rules apply, this attribute
* behaves like DefaultTransactionAttribute (rolling back on runtime exceptions).
*
* <p>{@link TransactionAttributeEditor} creates objects of this class.

117
src/docs/asciidoc/data-access.adoc

@ -184,7 +184,7 @@ transaction management. The following listing shows the definition of the @@ -184,7 +184,7 @@ transaction management. The following listing shows the definition of the
@Throws(TransactionException::class)
fun rollback(status: TransactionStatus)
}
----
----
This is primarily a service provider interface (SPI), although you can use it
<<transaction-programmatic-ptm, programmatically>> from your application code. Because
@ -241,7 +241,7 @@ listing shows the transaction strategy defined by @@ -241,7 +241,7 @@ listing shows the transaction strategy defined by
@Throws(TransactionException::class)
fun rollback(status: ReactiveTransaction): Mono<Void>
}
----
----
The reactive transaction manager is primarily a service provider interface (SPI),
although you can use it <<transaction-programmatic-rtm, programmatically>> from your
@ -566,7 +566,7 @@ abstractions mentioned earlier. @@ -566,7 +566,7 @@ abstractions mentioned earlier.
[[transaction-declarative]]
=== Declarative transaction management
=== Declarative Transaction Management
NOTE: Most Spring Framework users choose declarative transaction management. This option has
the least impact on application code and, hence, is most consistent with the ideals of a
@ -637,7 +637,7 @@ around method invocations. @@ -637,7 +637,7 @@ around method invocations.
NOTE: Spring AOP is covered in <<core.adoc#aop, the AOP section>>.
Spring Frameworks's `TransactionInterceptor` provides transaction management for
Spring Framework's `TransactionInterceptor` provides transaction management for
imperative and reactive programming models. The interceptor detects the desired flavor of
transaction management by inspecting the method return type. Methods returning a reactive
type such as `Publisher` or Kotlin `Flow` (or a subtype of those) qualify for reactive
@ -648,6 +648,18 @@ Transaction management flavors impact which transaction manager is required. Imp @@ -648,6 +648,18 @@ Transaction management flavors impact which transaction manager is required. Imp
transactions require a `PlatformTransactionManager`, while reactive transactions use
`ReactiveTransactionManager` implementations.
[NOTE]
====
`@Transactional` commonly works with thread-bound transactions managed by
`PlatformTransactionManager`, exposing a transaction to all data access operations within
the current execution thread. Note: This does _not_ propagate to newly started threads
within the method.
A reactive transaction managed by `ReactiveTransactionManager` uses the Reactor context
instead of thread-local attributes. As a consequence, all participating data access
operations need to execute within the same Reactor context in the same reactive pipeline.
====
The following image shows a conceptual view of calling a method on a transactional proxy:
image::images/tx.png[]
@ -1737,7 +1749,7 @@ in the application context: @@ -1737,7 +1749,7 @@ in the application context:
@Transactional("account")
public void doSomething() { ... }
@Transactional("reactive-account")
public Mono<Void> doSomethingReactive() { ... }
}
@ -2442,7 +2454,7 @@ the `TransactionOperator` resembles the next example: @@ -2442,7 +2454,7 @@ the `TransactionOperator` resembles the next example:
// the code in this method runs in a transactional context
Mono<Object> update = updateOperation1();
return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional);
}
}
@ -2529,7 +2541,7 @@ following example shows customization of the transactional settings for a specif @@ -2529,7 +2541,7 @@ following example shows customization of the transactional settings for a specif
public SimpleService(ReactiveTransactionManager transactionManager) {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// the transaction settings can be set here explicitly if so desired
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
definition.setTimeout(30); // 30 seconds
@ -2627,7 +2639,7 @@ following example shows how to do so: @@ -2627,7 +2639,7 @@ following example shows how to do so:
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def);
reactiveTx.flatMap(status -> {
Mono<Object> tx = ...; // put your business logic here
@ -2841,30 +2853,29 @@ specific to each technology. @@ -2841,30 +2853,29 @@ specific to each technology.
Spring provides a convenient translation from technology-specific exceptions, such as
`SQLException` to its own exception class hierarchy, which has `DataAccessException` as
the root exception. These exceptions wrap the original exception so that there is never any
risk that you might lose any information about what might have gone wrong.
the root exception. These exceptions wrap the original exception so that there is never
any risk that you might lose any information about what might have gone wrong.
In addition to JDBC exceptions, Spring can also wrap JPA- and Hibernate-specific exceptions,
converting them to a set of focused runtime exceptions.
This lets you handle most non-recoverable persistence exceptions
in only the appropriate layers, without having annoying boilerplate
catch-and-throw blocks and exception declarations in your DAOs. (You can still trap
and handle exceptions anywhere you need to though.) As mentioned above, JDBC
exceptions (including database-specific dialects) are also converted to the same
converting them to a set of focused runtime exceptions. This lets you handle most
non-recoverable persistence exceptions in only the appropriate layers, without having
annoying boilerplate catch-and-throw blocks and exception declarations in your DAOs.
(You can still trap and handle exceptions anywhere you need to though.) As mentioned above,
JDBC exceptions (including database-specific dialects) are also converted to the same
hierarchy, meaning that you can perform some operations with JDBC within a consistent
programming model.
The preceding discussion holds true for the various template classes in Spring's support for various ORM
frameworks. If you use the interceptor-based classes, the application must care
about handling `HibernateExceptions` and `PersistenceExceptions` itself, preferably by
delegating to the `convertHibernateAccessException(..)` or
`convertJpaAccessException()` methods, respectively, of `SessionFactoryUtils`. These methods convert the exceptions
The preceding discussion holds true for the various template classes in Spring's support
for various ORM frameworks. If you use the interceptor-based classes, the application must
care about handling `HibernateExceptions` and `PersistenceExceptions` itself, preferably by
delegating to the `convertHibernateAccessException(..)` or `convertJpaAccessException(..)`
methods, respectively, of `SessionFactoryUtils`. These methods convert the exceptions
to exceptions that are compatible with the exceptions in the `org.springframework.dao`
exception hierarchy. As `PersistenceExceptions` are unchecked, they can get
thrown, too (sacrificing generic DAO abstraction in terms of exceptions, though).
exception hierarchy. As `PersistenceExceptions` are unchecked, they can get thrown, too
(sacrificing generic DAO abstraction in terms of exceptions, though).
The following image shows the exception hierarchy that Spring provides. (Note that the
class hierarchy detailed in the image shows only a subset of the entire
The following image shows the exception hierarchy that Spring provides.
(Note that the class hierarchy detailed in the image shows only a subset of the entire
`DataAccessException` hierarchy.)
image::images/DataAccessException.png[]
@ -2989,7 +3000,7 @@ this `DataSource`. The following example autowires a `DataSource`: @@ -2989,7 +3000,7 @@ this `DataSource`. The following example autowires a `DataSource`:
----
@Repository
class JdbcMovieFinder(dataSource: DataSource) : MovieFinder {
private val jdbcTemplate = JdbcTemplate(dataSource)
// ...
@ -3250,8 +3261,8 @@ The following query finds and populates a single domain object: @@ -3250,8 +3261,8 @@ The following query finds and populates a single domain object:
----
val actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
arrayOf(1212L)) { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
arrayOf(1212L)) { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
----
@ -3503,7 +3514,7 @@ method with `@Autowired`. The following example shows how to do so: @@ -3503,7 +3514,7 @@ method with `@Autowired`. The following example shows how to do so:
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { // <2>
private val jdbcTemplate = JdbcTemplate(dataSource) // <3>
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
@ -3842,10 +3853,10 @@ translator: @@ -3842,10 +3853,10 @@ translator:
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
// create a custom translator and set the DataSource for the default translation lookup
exceptionTranslator = CustomSQLErrorCodesTranslator().apply {
this.dataSource = dataSource
this.dataSource = dataSource
}
}
fun updateShippingCharge(orderId: Long, pct: Long) {
// use the prepared JdbcTemplate for this update
this.jdbcTemplate!!.update("update orders" +
@ -4069,8 +4080,8 @@ on Oracle but may not work on other platforms: @@ -4069,8 +4080,8 @@ on Oracle but may not work on other platforms:
val name = "Rob"
val keyHolder = GeneratedKeyHolder()
jdbcTemplate.update({
it.prepareStatement (INSERT_SQL, arrayOf("id")).apply { setString(1, name) }
jdbcTemplate.update({
it.prepareStatement (INSERT_SQL, arrayOf("id")).apply { setString(1, name) }
}, keyHolder)
// keyHolder.getKey() now contains the generated key
@ -4229,14 +4240,14 @@ interface that wraps a single `Connection` that is not closed after each use. @@ -4229,14 +4240,14 @@ interface that wraps a single `Connection` that is not closed after each use.
This is not multi-threading capable.
If any client code calls `close` on the assumption of a pooled connection (as when using
persistence tools), you should set the `suppressClose` property to `true`. This setting returns a
close-suppressing proxy that wraps the physical connection. Note that you can no longer
cast this to a native Oracle `Connection` or a similar object.
persistence tools), you should set the `suppressClose` property to `true`. This setting
returns a close-suppressing proxy that wraps the physical connection. Note that you can
no longer cast this to a native Oracle `Connection` or a similar object.
`SingleConnectionDataSource` is primarily a test class. For example, it enables easy testing of code outside an
application server, in conjunction with a simple JNDI environment. In contrast to
`DriverManagerDataSource`, it reuses the same connection all the time, avoiding
excessive creation of physical connections.
`SingleConnectionDataSource` is primarily a test class. It typically enables easy testing
of code outside an application server, in conjunction with a simple JNDI environment.
In contrast to `DriverManagerDataSource`, it reuses the same connection all the time,
avoiding excessive creation of physical connections.
@ -5008,7 +5019,7 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi @@ -5008,7 +5019,7 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
private var procReadActor = SimpleJdbcCall(JdbcTemplate(dataSource).apply {
isResultsMapCaseInsensitive = true
}).withProcedureName("read_actor")
// ... additional methods
}
----
@ -5766,7 +5777,7 @@ the supplied `ResultSet`, as follows: @@ -5766,7 +5777,7 @@ the supplied `ResultSet`, as follows:
import org.springframework.jdbc.core.RowMapper
class GenreMapper : RowMapper<Genre> {
override fun mapRow(rs: ResultSet, rowNum: Int): Genre {
return Genre(rs.getString("name"))
}
@ -6777,7 +6788,7 @@ chapter then cover the other ORM technologies and show brief examples. @@ -6777,7 +6788,7 @@ chapter then cover the other ORM technologies and show brief examples.
NOTE: As of Spring Framework 5.0, Spring requires Hibernate ORM 4.3 or later for JPA support
and even Hibernate ORM 5.0+ for programming against the native Hibernate Session API.
Note that the Hibernate team does not maintain any versions prior to 5.1 anymore and
is likely to focus on 5.3+ exclusively soon.
is likely to focus on 5.4+ exclusively soon.
[[orm-session-factory-setup]]
@ -6884,7 +6895,7 @@ implementation resembles the following example, based on the plain Hibernate API @@ -6884,7 +6895,7 @@ implementation resembles the following example, based on the plain Hibernate API
.Kotlin
----
class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao {
fun loadProductsByCategory(category: String): Collection<*> {
return sessionFactory.currentSession
.createQuery("from test.Product product where product.category=?")
@ -7092,7 +7103,7 @@ and an example for a business method implementation: @@ -7092,7 +7103,7 @@ and an example for a business method implementation:
----
class ProductServiceImpl(transactionManager: PlatformTransactionManager,
private val productDao: ProductDao) : ProductService {
private val transactionTemplate = TransactionTemplate(transactionManager)
fun increasePriceOfAllProductsInCategory(category: String) {
@ -7354,7 +7365,7 @@ This includes web containers such as Tomcat, stand-alone applications, and @@ -7354,7 +7365,7 @@ This includes web containers such as Tomcat, stand-alone applications, and
integration tests with sophisticated persistence requirements.
NOTE: If you want to specifically configure a Hibernate setup, an immediate alternative is
to go with Hibernate 5.2 or 5.3 and set up a native Hibernate `LocalSessionFactoryBean`
to go with Hibernate 5.2/5.3/5.4 and set up a native Hibernate `LocalSessionFactoryBean`
instead of a plain JPA `LocalContainerEntityManagerFactoryBean`, letting it interact
with JPA access code as well as native Hibernate access code.
See <<orm-jpa-hibernate, Native Hibernate setup for JPA interaction>> for details.
@ -7726,7 +7737,7 @@ Spring provides dialects for the EclipseLink and Hibernate JPA implementations. @@ -7726,7 +7737,7 @@ Spring provides dialects for the EclipseLink and Hibernate JPA implementations.
See the <<orm-jpa-dialect, next section>> for details on the `JpaDialect` mechanism.
NOTE: As an immediate alternative, Spring's native `HibernateTransactionManager` is capable
of interacting with JPA access code as of Spring Framework 5.1 and Hibernate 5.2/5.3,
of interacting with JPA access code as of Spring Framework 5.1 and Hibernate 5.2/5.3/5.4,
adapting to several Hibernate specifics and providing JDBC interaction.
This makes particular sense in combination with `LocalSessionFactoryBean` setup.
See <<orm-jpa-hibernate, Native Hibernate Setup for JPA Interaction>> for details.
@ -7801,7 +7812,7 @@ less portable) but is set up for the server's JTA environment. @@ -7801,7 +7812,7 @@ less portable) but is set up for the server's JTA environment.
[[orm-jpa-hibernate]]
==== Native Hibernate Setup and Native Hibernate Transactions for JPA Interaction
As of Spring Framework 5.1 and Hibernate 5.2/5.3, a native `LocalSessionFactoryBean`
As of Spring Framework 5.1 and Hibernate 5.2/5.3/5.4, a native `LocalSessionFactoryBean`
setup in combination with `HibernateTransactionManager` allows for interaction with
`@PersistenceContext` and other JPA access code. A Hibernate
`SessionFactory` natively implements JPA's `EntityManagerFactory` interface now
@ -8160,8 +8171,8 @@ can do so by using the following `applicationContext.xml`: @@ -8160,8 +8171,8 @@ can do so by using the following `applicationContext.xml`:
----
This application context uses XStream, but we could have used any of the other marshaller
instances described later in this chapter. Note that, by default, XStream does not require any further
configuration, so the bean definition is rather simple. Also note that the
instances described later in this chapter. Note that, by default, XStream does not require
any further configuration, so the bean definition is rather simple. Also note that the
`XStreamMarshaller` implements both `Marshaller` and `Unmarshaller`, so we can refer to the
`xstreamMarshaller` bean in both the `marshaller` and `unmarshaller` property of the
application.
@ -8179,8 +8190,8 @@ This sample application produces the following `settings.xml` file: @@ -8179,8 +8190,8 @@ This sample application produces the following `settings.xml` file:
[[oxm-schema-based-config]]
=== XML Configuration Namespace
You can configure marshallers more concisely by using tags from the OXM namespace. To
make these tags available, you must first reference the appropriate schema in the
You can configure marshallers more concisely by using tags from the OXM namespace.
To make these tags available, you must first reference the appropriate schema in the
preamble of the XML configuration file. The following example shows how to do so:
[source,xml,indent=0]
@ -8423,7 +8434,7 @@ vulnerabilities do not get invoked. @@ -8423,7 +8434,7 @@ vulnerabilities do not get invoked.
NOTE: Note that XStream is an XML serialization library, not a data binding library.
Therefore, it has limited namespace support. As a result, it is rather unsuitable for usage
within Web services.
within Web Services.

Loading…
Cancel
Save