Skip to content

Commit 322babc

Browse files
committed
Document that @transactional does not propagate to new threads
Closes gh-25439
1 parent 4e720e8 commit 322babc

File tree

3 files changed

+80
-60
lines changed

3 files changed

+80
-60
lines changed

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,25 @@
3838
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
3939
* class, and in fact {@link AnnotationTransactionAttributeSource} will directly
4040
* convert the data to the latter class, so that Spring's transaction support code
41-
* does not have to know about annotations. If no rules are relevant to the exception,
42-
* it will be treated like
43-
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
44-
* (rolling back on {@link RuntimeException} and {@link Error} but not on checked
45-
* exceptions).
41+
* does not have to know about annotations. If no custom rollback rules apply,
42+
* the transaction will roll back on {@link RuntimeException} and {@link Error}
43+
* but not on checked exceptions.
4644
*
4745
* <p>For specific information about the semantics of this annotation's attributes,
4846
* consult the {@link org.springframework.transaction.TransactionDefinition} and
4947
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
5048
*
49+
* <p>This annotation commonly works with thread-bound transactions managed by
50+
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
51+
* transaction to all data access operations within the current execution thread.
52+
* <b>Note: This does NOT propagate to newly started threads within the method.</b>
53+
*
54+
* <p>Alternatively, this annotation may demarcate a reactive transaction managed
55+
* by {@link org.springframework.transaction.ReactiveTransactionManager} which
56+
* uses the Reactor context instead of thread-local attributes. As a consequence,
57+
* all participating data access operations need to execute within the same
58+
* Reactor context in the same reactive pipeline.
59+
*
5160
* @author Colin Sampaleanu
5261
* @author Juergen Hoeller
5362
* @author Sam Brannen

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
2929
/**
3030
* TransactionAttribute implementation that works out whether a given exception
3131
* should cause transaction rollback by applying a number of rollback rules,
32-
* both positive and negative. If no rules are relevant to the exception, it
32+
* both positive and negative. If no custom rollback rules apply, this attribute
3333
* behaves like DefaultTransactionAttribute (rolling back on runtime exceptions).
3434
*
3535
* <p>{@link TransactionAttributeEditor} creates objects of this class.

src/docs/asciidoc/data-access.adoc

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ transaction management. The following listing shows the definition of the
184184
@Throws(TransactionException::class)
185185
fun rollback(status: TransactionStatus)
186186
}
187-
----
187+
----
188188

189189
This is primarily a service provider interface (SPI), although you can use it
190190
<<transaction-programmatic-ptm, programmatically>> from your application code. Because
@@ -241,7 +241,7 @@ listing shows the transaction strategy defined by
241241
@Throws(TransactionException::class)
242242
fun rollback(status: ReactiveTransaction): Mono<Void>
243243
}
244-
----
244+
----
245245

246246
The reactive transaction manager is primarily a service provider interface (SPI),
247247
although you can use it <<transaction-programmatic-rtm, programmatically>> from your
@@ -566,7 +566,7 @@ abstractions mentioned earlier.
566566

567567

568568
[[transaction-declarative]]
569-
=== Declarative transaction management
569+
=== Declarative Transaction Management
570570

571571
NOTE: Most Spring Framework users choose declarative transaction management. This option has
572572
the least impact on application code and, hence, is most consistent with the ideals of a
@@ -637,7 +637,7 @@ around method invocations.
637637

638638
NOTE: Spring AOP is covered in <<core.adoc#aop, the AOP section>>.
639639

640-
Spring Frameworks's `TransactionInterceptor` provides transaction management for
640+
Spring Framework's `TransactionInterceptor` provides transaction management for
641641
imperative and reactive programming models. The interceptor detects the desired flavor of
642642
transaction management by inspecting the method return type. Methods returning a reactive
643643
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
648648
transactions require a `PlatformTransactionManager`, while reactive transactions use
649649
`ReactiveTransactionManager` implementations.
650650

651+
[NOTE]
652+
====
653+
`@Transactional` commonly works with thread-bound transactions managed by
654+
`PlatformTransactionManager`, exposing a transaction to all data access operations within
655+
the current execution thread. Note: This does _not_ propagate to newly started threads
656+
within the method.
657+
658+
A reactive transaction managed by `ReactiveTransactionManager` uses the Reactor context
659+
instead of thread-local attributes. As a consequence, all participating data access
660+
operations need to execute within the same Reactor context in the same reactive pipeline.
661+
====
662+
651663
The following image shows a conceptual view of calling a method on a transactional proxy:
652664

653665
image::images/tx.png[]
@@ -1737,7 +1749,7 @@ in the application context:
17371749
17381750
@Transactional("account")
17391751
public void doSomething() { ... }
1740-
1752+
17411753
@Transactional("reactive-account")
17421754
public Mono<Void> doSomethingReactive() { ... }
17431755
}
@@ -2442,7 +2454,7 @@ the `TransactionOperator` resembles the next example:
24422454
// the code in this method runs in a transactional context
24432455
24442456
Mono<Object> update = updateOperation1();
2445-
2457+
24462458
return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional);
24472459
}
24482460
}
@@ -2529,7 +2541,7 @@ following example shows customization of the transactional settings for a specif
25292541
25302542
public SimpleService(ReactiveTransactionManager transactionManager) {
25312543
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
2532-
2544+
25332545
// the transaction settings can be set here explicitly if so desired
25342546
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
25352547
definition.setTimeout(30); // 30 seconds
@@ -2627,7 +2639,7 @@ following example shows how to do so:
26272639
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
26282640
26292641
Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def);
2630-
2642+
26312643
reactiveTx.flatMap(status -> {
26322644
26332645
Mono<Object> tx = ...; // put your business logic here
@@ -2841,30 +2853,29 @@ specific to each technology.
28412853

28422854
Spring provides a convenient translation from technology-specific exceptions, such as
28432855
`SQLException` to its own exception class hierarchy, which has `DataAccessException` as
2844-
the root exception. These exceptions wrap the original exception so that there is never any
2845-
risk that you might lose any information about what might have gone wrong.
2856+
the root exception. These exceptions wrap the original exception so that there is never
2857+
any risk that you might lose any information about what might have gone wrong.
28462858

28472859
In addition to JDBC exceptions, Spring can also wrap JPA- and Hibernate-specific exceptions,
2848-
converting them to a set of focused runtime exceptions.
2849-
This lets you handle most non-recoverable persistence exceptions
2850-
in only the appropriate layers, without having annoying boilerplate
2851-
catch-and-throw blocks and exception declarations in your DAOs. (You can still trap
2852-
and handle exceptions anywhere you need to though.) As mentioned above, JDBC
2853-
exceptions (including database-specific dialects) are also converted to the same
2860+
converting them to a set of focused runtime exceptions. This lets you handle most
2861+
non-recoverable persistence exceptions in only the appropriate layers, without having
2862+
annoying boilerplate catch-and-throw blocks and exception declarations in your DAOs.
2863+
(You can still trap and handle exceptions anywhere you need to though.) As mentioned above,
2864+
JDBC exceptions (including database-specific dialects) are also converted to the same
28542865
hierarchy, meaning that you can perform some operations with JDBC within a consistent
28552866
programming model.
28562867

2857-
The preceding discussion holds true for the various template classes in Spring's support for various ORM
2858-
frameworks. If you use the interceptor-based classes, the application must care
2859-
about handling `HibernateExceptions` and `PersistenceExceptions` itself, preferably by
2860-
delegating to the `convertHibernateAccessException(..)` or
2861-
`convertJpaAccessException()` methods, respectively, of `SessionFactoryUtils`. These methods convert the exceptions
2868+
The preceding discussion holds true for the various template classes in Spring's support
2869+
for various ORM frameworks. If you use the interceptor-based classes, the application must
2870+
care about handling `HibernateExceptions` and `PersistenceExceptions` itself, preferably by
2871+
delegating to the `convertHibernateAccessException(..)` or `convertJpaAccessException(..)`
2872+
methods, respectively, of `SessionFactoryUtils`. These methods convert the exceptions
28622873
to exceptions that are compatible with the exceptions in the `org.springframework.dao`
2863-
exception hierarchy. As `PersistenceExceptions` are unchecked, they can get
2864-
thrown, too (sacrificing generic DAO abstraction in terms of exceptions, though).
2874+
exception hierarchy. As `PersistenceExceptions` are unchecked, they can get thrown, too
2875+
(sacrificing generic DAO abstraction in terms of exceptions, though).
28652876

2866-
The following image shows the exception hierarchy that Spring provides. (Note that the
2867-
class hierarchy detailed in the image shows only a subset of the entire
2877+
The following image shows the exception hierarchy that Spring provides.
2878+
(Note that the class hierarchy detailed in the image shows only a subset of the entire
28682879
`DataAccessException` hierarchy.)
28692880

28702881
image::images/DataAccessException.png[]
@@ -2989,7 +3000,7 @@ this `DataSource`. The following example autowires a `DataSource`:
29893000
----
29903001
@Repository
29913002
class JdbcMovieFinder(dataSource: DataSource) : MovieFinder {
2992-
3003+
29933004
private val jdbcTemplate = JdbcTemplate(dataSource)
29943005
29953006
// ...
@@ -3250,8 +3261,8 @@ The following query finds and populates a single domain object:
32503261
----
32513262
val actor = jdbcTemplate.queryForObject(
32523263
"select first_name, last_name from t_actor where id = ?",
3253-
arrayOf(1212L)) { rs, _ ->
3254-
Actor(rs.getString("first_name"), rs.getString("last_name"))
3264+
arrayOf(1212L)) { rs, _ ->
3265+
Actor(rs.getString("first_name"), rs.getString("last_name"))
32553266
}
32563267
----
32573268

@@ -3503,7 +3514,7 @@ method with `@Autowired`. The following example shows how to do so:
35033514
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { // <2>
35043515
35053516
private val jdbcTemplate = JdbcTemplate(dataSource) // <3>
3506-
3517+
35073518
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
35083519
}
35093520
----
@@ -3842,10 +3853,10 @@ translator:
38423853
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
38433854
// create a custom translator and set the DataSource for the default translation lookup
38443855
exceptionTranslator = CustomSQLErrorCodesTranslator().apply {
3845-
this.dataSource = dataSource
3856+
this.dataSource = dataSource
38463857
}
38473858
}
3848-
3859+
38493860
fun updateShippingCharge(orderId: Long, pct: Long) {
38503861
// use the prepared JdbcTemplate for this update
38513862
this.jdbcTemplate!!.update("update orders" +
@@ -4069,8 +4080,8 @@ on Oracle but may not work on other platforms:
40694080
val name = "Rob"
40704081
40714082
val keyHolder = GeneratedKeyHolder()
4072-
jdbcTemplate.update({
4073-
it.prepareStatement (INSERT_SQL, arrayOf("id")).apply { setString(1, name) }
4083+
jdbcTemplate.update({
4084+
it.prepareStatement (INSERT_SQL, arrayOf("id")).apply { setString(1, name) }
40744085
}, keyHolder)
40754086
40764087
// keyHolder.getKey() now contains the generated key
@@ -4229,14 +4240,14 @@ interface that wraps a single `Connection` that is not closed after each use.
42294240
This is not multi-threading capable.
42304241

42314242
If any client code calls `close` on the assumption of a pooled connection (as when using
4232-
persistence tools), you should set the `suppressClose` property to `true`. This setting returns a
4233-
close-suppressing proxy that wraps the physical connection. Note that you can no longer
4234-
cast this to a native Oracle `Connection` or a similar object.
4243+
persistence tools), you should set the `suppressClose` property to `true`. This setting
4244+
returns a close-suppressing proxy that wraps the physical connection. Note that you can
4245+
no longer cast this to a native Oracle `Connection` or a similar object.
42354246

4236-
`SingleConnectionDataSource` is primarily a test class. For example, it enables easy testing of code outside an
4237-
application server, in conjunction with a simple JNDI environment. In contrast to
4238-
`DriverManagerDataSource`, it reuses the same connection all the time, avoiding
4239-
excessive creation of physical connections.
4247+
`SingleConnectionDataSource` is primarily a test class. It typically enables easy testing
4248+
of code outside an application server, in conjunction with a simple JNDI environment.
4249+
In contrast to `DriverManagerDataSource`, it reuses the same connection all the time,
4250+
avoiding excessive creation of physical connections.
42404251

42414252

42424253

@@ -5008,7 +5019,7 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
50085019
private var procReadActor = SimpleJdbcCall(JdbcTemplate(dataSource).apply {
50095020
isResultsMapCaseInsensitive = true
50105021
}).withProcedureName("read_actor")
5011-
5022+
50125023
// ... additional methods
50135024
}
50145025
----
@@ -5766,7 +5777,7 @@ the supplied `ResultSet`, as follows:
57665777
import org.springframework.jdbc.core.RowMapper
57675778
57685779
class GenreMapper : RowMapper<Genre> {
5769-
5780+
57705781
override fun mapRow(rs: ResultSet, rowNum: Int): Genre {
57715782
return Genre(rs.getString("name"))
57725783
}
@@ -6777,7 +6788,7 @@ chapter then cover the other ORM technologies and show brief examples.
67776788
NOTE: As of Spring Framework 5.0, Spring requires Hibernate ORM 4.3 or later for JPA support
67786789
and even Hibernate ORM 5.0+ for programming against the native Hibernate Session API.
67796790
Note that the Hibernate team does not maintain any versions prior to 5.1 anymore and
6780-
is likely to focus on 5.3+ exclusively soon.
6791+
is likely to focus on 5.4+ exclusively soon.
67816792

67826793

67836794
[[orm-session-factory-setup]]
@@ -6884,7 +6895,7 @@ implementation resembles the following example, based on the plain Hibernate API
68846895
.Kotlin
68856896
----
68866897
class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao {
6887-
6898+
68886899
fun loadProductsByCategory(category: String): Collection<*> {
68896900
return sessionFactory.currentSession
68906901
.createQuery("from test.Product product where product.category=?")
@@ -7092,7 +7103,7 @@ and an example for a business method implementation:
70927103
----
70937104
class ProductServiceImpl(transactionManager: PlatformTransactionManager,
70947105
private val productDao: ProductDao) : ProductService {
7095-
7106+
70967107
private val transactionTemplate = TransactionTemplate(transactionManager)
70977108
70987109
fun increasePriceOfAllProductsInCategory(category: String) {
@@ -7354,7 +7365,7 @@ This includes web containers such as Tomcat, stand-alone applications, and
73547365
integration tests with sophisticated persistence requirements.
73557366

73567367
NOTE: If you want to specifically configure a Hibernate setup, an immediate alternative is
7357-
to go with Hibernate 5.2 or 5.3 and set up a native Hibernate `LocalSessionFactoryBean`
7368+
to go with Hibernate 5.2/5.3/5.4 and set up a native Hibernate `LocalSessionFactoryBean`
73587369
instead of a plain JPA `LocalContainerEntityManagerFactoryBean`, letting it interact
73597370
with JPA access code as well as native Hibernate access code.
73607371
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.
77267737
See the <<orm-jpa-dialect, next section>> for details on the `JpaDialect` mechanism.
77277738

77287739
NOTE: As an immediate alternative, Spring's native `HibernateTransactionManager` is capable
7729-
of interacting with JPA access code as of Spring Framework 5.1 and Hibernate 5.2/5.3,
7740+
of interacting with JPA access code as of Spring Framework 5.1 and Hibernate 5.2/5.3/5.4,
77307741
adapting to several Hibernate specifics and providing JDBC interaction.
77317742
This makes particular sense in combination with `LocalSessionFactoryBean` setup.
77327743
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.
78017812
[[orm-jpa-hibernate]]
78027813
==== Native Hibernate Setup and Native Hibernate Transactions for JPA Interaction
78037814

7804-
As of Spring Framework 5.1 and Hibernate 5.2/5.3, a native `LocalSessionFactoryBean`
7815+
As of Spring Framework 5.1 and Hibernate 5.2/5.3/5.4, a native `LocalSessionFactoryBean`
78057816
setup in combination with `HibernateTransactionManager` allows for interaction with
78067817
`@PersistenceContext` and other JPA access code. A Hibernate
78077818
`SessionFactory` natively implements JPA's `EntityManagerFactory` interface now
@@ -8160,8 +8171,8 @@ can do so by using the following `applicationContext.xml`:
81608171
----
81618172

81628173
This application context uses XStream, but we could have used any of the other marshaller
8163-
instances described later in this chapter. Note that, by default, XStream does not require any further
8164-
configuration, so the bean definition is rather simple. Also note that the
8174+
instances described later in this chapter. Note that, by default, XStream does not require
8175+
any further configuration, so the bean definition is rather simple. Also note that the
81658176
`XStreamMarshaller` implements both `Marshaller` and `Unmarshaller`, so we can refer to the
81668177
`xstreamMarshaller` bean in both the `marshaller` and `unmarshaller` property of the
81678178
application.
@@ -8179,8 +8190,8 @@ This sample application produces the following `settings.xml` file:
81798190
[[oxm-schema-based-config]]
81808191
=== XML Configuration Namespace
81818192

8182-
You can configure marshallers more concisely by using tags from the OXM namespace. To
8183-
make these tags available, you must first reference the appropriate schema in the
8193+
You can configure marshallers more concisely by using tags from the OXM namespace.
8194+
To make these tags available, you must first reference the appropriate schema in the
81848195
preamble of the XML configuration file. The following example shows how to do so:
81858196

81868197
[source,xml,indent=0]
@@ -8423,7 +8434,7 @@ vulnerabilities do not get invoked.
84238434

84248435
NOTE: Note that XStream is an XML serialization library, not a data binding library.
84258436
Therefore, it has limited namespace support. As a result, it is rather unsuitable for usage
8426-
within Web services.
8437+
within Web Services.
84278438

84288439

84298440

0 commit comments

Comments
 (0)