Migrate to Asciidoctor Tabs

This commit is contained in:
Rob Winch
2023-04-20 16:21:36 -05:00
committed by rstoyanchev
parent 71154fd16b
commit 39146f9066
243 changed files with 7124 additions and 1779 deletions

View File

@@ -52,14 +52,18 @@ lets the component scanning support find and configure your DAOs and repositorie
without having to provide XML configuration entries for them. The following example shows
how to use the `@Repository` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository // <1>
public class SomeMovieFinder implements MovieFinder {
// ...
}
----
======
<1> The `@Repository` annotation.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -80,8 +84,11 @@ needs access to a JDBC `DataSource`, and a JPA-based repository needs access to
injected by using one of the `@Autowired`, `@Inject`, `@Resource` or `@PersistenceContext`
annotations. The following example works for a JPA repository:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class JpaMovieFinder implements MovieFinder {
@@ -93,8 +100,9 @@ annotations. The following example works for a JPA repository:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class JpaMovieFinder : MovieFinder {
@@ -105,13 +113,17 @@ annotations. The following example works for a JPA repository:
// ...
}
----
======
If you use the classic Hibernate APIs, you can inject `SessionFactory`, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class HibernateMovieFinder implements MovieFinder {
@@ -126,22 +138,28 @@ example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class HibernateMovieFinder(private val sessionFactory: SessionFactory) : MovieFinder {
// ...
}
----
======
The last example we show here is for typical JDBC support. You could have the
`DataSource` injected into an initialization method or a constructor, where you would create a
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using
this `DataSource`. The following example autowires a `DataSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class JdbcMovieFinder implements MovieFinder {
@@ -156,8 +174,10 @@ this `DataSource`. The following example autowires a `DataSource`:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class JdbcMovieFinder(dataSource: DataSource) : MovieFinder {
@@ -167,6 +187,7 @@ this `DataSource`. The following example autowires a `DataSource`:
// ...
}
----
======
NOTE: See the specific coverage of each persistence technology for details on how to
configure the application context to take advantage of these annotations.

View File

@@ -17,8 +17,11 @@ the prepared statement. This method is called the number of times that you
specified in the `getBatchSize` call. The following example updates the `t_actor` table
based on entries in a list, and the entire list is used as the batch:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -47,8 +50,10 @@ based on entries in a list, and the entire list is used as the batch:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -71,6 +76,7 @@ based on entries in a list, and the entire list is used as the batch:
// ... additional methods
}
----
======
If you process a stream of updates or reading from a file, you might have a
preferred batch size, but the last batch might not have that number of entries. In this
@@ -94,8 +100,11 @@ in an array of bean-style objects (with getter methods corresponding to paramete
The following example shows a batch update using named parameters:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -114,8 +123,10 @@ The following example shows a batch update using named parameters:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -130,6 +141,7 @@ The following example shows a batch update using named parameters:
// ... additional methods
}
----
======
For an SQL statement that uses the classic `?` placeholders, you pass in a list
containing an object array with the update values. This object array must have one entry
@@ -139,8 +151,11 @@ defined in the SQL statement.
The following example is the same as the preceding example, except that it uses classic
JDBC `?` placeholders:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -165,8 +180,10 @@ JDBC `?` placeholders:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -184,6 +201,7 @@ JDBC `?` placeholders:
// ... additional methods
}
----
======
All of the batch update methods that we described earlier return an `int` array
containing the number of affected rows for each batch entry. This count is reported by
@@ -223,8 +241,11 @@ update calls into batches of the size specified.
The following example shows a batch update that uses a batch size of 100:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -250,8 +271,10 @@ The following example shows a batch update that uses a batch size of 100:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -270,6 +293,7 @@ The following example shows a batch update that uses a batch size of 100:
// ... additional methods
}
----
======
The batch update method for this call returns an array of `int` arrays that contains an
array entry for each batch with an array of the number of affected rows for each update.

View File

@@ -48,8 +48,11 @@ To configure a `DriverManagerDataSource`:
The following example shows how to configure a `DriverManagerDataSource` in Java:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
@@ -57,8 +60,10 @@ The following example shows how to configure a `DriverManagerDataSource` in Java
dataSource.setUsername("sa");
dataSource.setPassword("");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val dataSource = DriverManagerDataSource().apply {
setDriverClassName("org.hsqldb.jdbcDriver")
@@ -67,6 +72,7 @@ The following example shows how to configure a `DriverManagerDataSource` in Java
password = ""
}
----
======
The following example shows the corresponding XML configuration:

View File

@@ -57,54 +57,75 @@ See the attendant {api-spring-framework}/jdbc/core/JdbcTemplate.html[javadoc] fo
The following query gets the number of rows in a relation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!
----
======
The following query uses a bind variable:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val countOfActorsNamedJoe = jdbcTemplate.queryForObject<Int>(
"select count(*) from t_actor where first_name = ?", arrayOf("Joe"))!!
----
======
The following query looks for a `String`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val lastName = this.jdbcTemplate.queryForObject<String>(
"select last_name from t_actor where id = ?",
arrayOf(1212L))!!
----
======
The following query finds and populates a single domain object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
@@ -116,8 +137,10 @@ The following query finds and populates a single domain object:
},
1212L);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
@@ -125,11 +148,15 @@ The following query finds and populates a single domain object:
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
----
======
The following query finds and populates a list of domain objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
@@ -140,20 +167,26 @@ The following query finds and populates a list of domain objects:
return actor;
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
----
======
If the last two snippets of code actually existed in the same application, it would make
sense to remove the duplication present in the two `RowMapper` lambda expressions and
extract them out into a single field that could then be referenced by DAO methods as needed.
For example, it may be better to write the preceding code snippet as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
@@ -166,8 +199,10 @@ For example, it may be better to write the preceding code snippet as follows:
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
@@ -177,6 +212,7 @@ For example, it may be better to write the preceding code snippet as follows:
return jdbcTemplate.query("select first_name, last_name from t_actor", actorMapper)
}
----
======
[[jdbc-JdbcTemplate-examples-update]]
=== Updating (`INSERT`, `UPDATE`, and `DELETE`) with `JdbcTemplate`
@@ -186,52 +222,70 @@ Parameter values are usually provided as variable arguments or, alternatively, a
The following example inserts a new entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling")
----
======
The following example updates an existing entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L)
----
======
The following example deletes an entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong())
----
======
[[jdbc-JdbcTemplate-examples-other]]
=== Other `JdbcTemplate` Operations
@@ -241,33 +295,45 @@ method is often used for DDL statements. It is heavily overloaded with variants
callback interfaces, binding variable arrays, and so on. The following example creates a
table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
----
======
The following example invokes a stored procedure:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
unionId.toLong())
----
======
More sophisticated stored procedure support is xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[covered later].
@@ -288,8 +354,11 @@ that shared `DataSource` bean into your DAO classes. The `JdbcTemplate` is creat
the setter for the `DataSource`. This leads to DAOs that resemble the following:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcCorporateEventDao implements CorporateEventDao {
@@ -302,8 +371,10 @@ the setter for the `DataSource`. This leads to DAOs that resemble the following:
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao {
@@ -312,6 +383,7 @@ the setter for the `DataSource`. This leads to DAOs that resemble the following:
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
--
The following example shows the corresponding XML configuration:
@@ -350,8 +422,11 @@ support for dependency injection. In this case, you can annotate the class with
method with `@Autowired`. The following example shows how to do so:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository // <1>
public class JdbcCorporateEventDao implements CorporateEventDao {
@@ -366,6 +441,7 @@ method with `@Autowired`. The following example shows how to do so:
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
<1> Annotate the class with `@Repository`.
<2> Annotate the `DataSource` setter method with `@Autowired`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
@@ -440,8 +516,11 @@ section describes only those areas of the `NamedParameterJdbcTemplate` class tha
from the `JdbcTemplate` itself -- namely, programming JDBC statements by using named
parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@@ -460,8 +539,9 @@ parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
@@ -471,6 +551,7 @@ parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
----
======
Notice the use of the named parameter notation in the value assigned to the `sql`
variable and the corresponding value that is plugged into the `namedParameters`
@@ -483,8 +564,11 @@ methods exposed by the `NamedParameterJdbcOperations` and implemented by the
The following example shows the use of the `Map`-based style:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@@ -502,8 +586,10 @@ The following example shows the use of the `Map`-based style:
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
@@ -514,6 +600,7 @@ The following example shows the use of the `Map`-based style:
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
----
======
One nice feature related to the `NamedParameterJdbcTemplate` (and existing in the same
Java package) is the `SqlParameterSource` interface. You have already seen an example of
@@ -531,8 +618,11 @@ of named parameter values.
The following example shows a typical JavaBean:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Actor {
@@ -556,17 +646,23 @@ The following example shows a typical JavaBean:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
data class Actor(val id: Long, val firstName: String, val lastName: String)
----
======
The following example uses a `NamedParameterJdbcTemplate` to return the count of the
members of the class shown in the preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@@ -585,8 +681,10 @@ members of the class shown in the preceding example:
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
@@ -600,6 +698,7 @@ members of the class shown in the preceding example:
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
----
======
Remember that the `NamedParameterJdbcTemplate` class wraps a classic `JdbcTemplate`
template. If you need access to the wrapped `JdbcTemplate` instance to access
@@ -651,8 +750,11 @@ name from the database metadata of the database in use.
You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
@@ -664,8 +766,10 @@ You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example sh
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CustomSQLErrorCodesTranslator : SQLErrorCodeSQLExceptionTranslator() {
@@ -677,6 +781,7 @@ You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example sh
}
}
----
======
In the preceding example, the specific error code (`-12345`) is translated, while other errors are
left to be translated by the default translator implementation. To use this custom
@@ -685,8 +790,11 @@ translator, you must pass it to the `JdbcTemplate` through the method
processing where this translator is needed. The following example shows how you can use this custom
translator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private JdbcTemplate jdbcTemplate;
@@ -710,8 +818,10 @@ translator:
" where id = ?", pct, orderId);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// create a JdbcTemplate and set data source
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
@@ -728,6 +838,7 @@ translator:
" where id = ?", pct, orderId)
}
----
======
The custom translator is passed a data source in order to look up the error codes in
`sql-error-codes.xml`.
@@ -741,8 +852,11 @@ Running an SQL statement requires very little code. You need a `DataSource` and
`JdbcTemplate`. The following example shows what you need to include for a minimal but
fully functional class that creates a new table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -760,8 +874,10 @@ fully functional class that creates a new table:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
@@ -775,6 +891,7 @@ fully functional class that creates a new table:
}
}
----
======
[[jdbc-statements-querying]]
@@ -786,8 +903,11 @@ Java class that is passed in as an argument. If the type conversion is invalid,
`InvalidDataAccessApiUsageException` is thrown. The following example contains two
query methods, one for an `int` and one that queries for a `String`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -809,8 +929,10 @@ query methods, one for an `int` and one that queries for a `String`:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
@@ -826,6 +948,7 @@ class RunAQuery(dataSource: DataSource) {
get() = jdbcTemplate.queryForObject("select name from mytable")
}
----
======
In addition to the single result query methods, several methods return a list with an
entry for each row that the query returned. The most generic method is `queryForList(..)`,
@@ -833,8 +956,11 @@ which returns a `List` where each element is a `Map` containing one entry for ea
using the column name as the key. If you add a method to the preceding example to retrieve a
list of all the rows, it might be as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private JdbcTemplate jdbcTemplate;
@@ -846,8 +972,10 @@ list of all the rows, it might be as follows:
return this.jdbcTemplate.queryForList("select * from mytable");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
private val jdbcTemplate = JdbcTemplate(dataSource)
@@ -855,6 +983,7 @@ list of all the rows, it might be as follows:
return jdbcTemplate.queryForList("select * from mytable")
}
----
======
The returned list would resemble the following:
@@ -869,8 +998,11 @@ The returned list would resemble the following:
The following example updates a column for a certain primary key:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -888,8 +1020,10 @@ The following example updates a column for a certain primary key:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
@@ -903,6 +1037,7 @@ The following example updates a column for a certain primary key:
}
}
----
======
In the preceding example,
an SQL statement has placeholders for row parameters. You can pass the parameter values
@@ -922,8 +1057,11 @@ update. There is no standard single way to create an appropriate `PreparedStatem
(which explains why the method signature is the way it is). The following example works
on Oracle but may not work on other platforms:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
@@ -937,8 +1075,10 @@ on Oracle but may not work on other platforms:
// keyHolder.getKey() now contains the generated key
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val INSERT_SQL = "insert into my_test (name) values(?)"
val name = "Rob"
@@ -950,6 +1090,7 @@ on Oracle but may not work on other platforms:
// keyHolder.getKey() now contains the generated key
----
======

View File

@@ -44,8 +44,11 @@ The `EmbeddedDatabaseBuilder` class provides a fluent API for constructing an em
database programmatically. You can use this when you need to create an embedded database in a
stand-alone environment or in a stand-alone integration test, as in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
@@ -60,8 +63,10 @@ stand-alone environment or in a stand-alone integration test, as in the followin
db.shutdown()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val db = EmbeddedDatabaseBuilder()
.generateUniqueName(true)
@@ -76,6 +81,7 @@ stand-alone environment or in a stand-alone integration test, as in the followin
db.shutdown()
----
======
See the {api-spring-framework}/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.html[javadoc for `EmbeddedDatabaseBuilder`]
for further details on all supported options.
@@ -83,8 +89,11 @@ for further details on all supported options.
You can also use the `EmbeddedDatabaseBuilder` to create an embedded database by using Java
configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class DataSourceConfig {
@@ -102,8 +111,10 @@ configuration, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class DataSourceConfig {
@@ -121,6 +132,7 @@ configuration, as the following example shows:
}
}
----
======
[[jdbc-embedded-database-types]]
@@ -168,8 +180,11 @@ configuring the embedded database as a bean in the Spring `ApplicationContext` a
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-xml[Creating an Embedded Database by Using Spring XML] and xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-java[Creating an Embedded Database Programmatically]. The following listing
shows the test template:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class DataAccessIntegrationTestTemplate {
@@ -198,8 +213,10 @@ shows the test template:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class DataAccessIntegrationTestTemplate {
@@ -227,6 +244,7 @@ shows the test template:
}
}
----
======
[[jdbc-embedded-database-unique-names]]

View File

@@ -40,8 +40,11 @@ abstract `mapRow(..)` method to convert each row of the supplied `ResultSet` int
object of the type specified. The following example shows a custom query that maps the
data from the `t_actor` relation to an instance of the `Actor` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ActorMappingQuery extends MappingSqlQuery<Actor> {
@@ -61,8 +64,10 @@ data from the `t_actor` relation to an instance of the `Actor` class:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ActorMappingQuery(ds: DataSource) : MappingSqlQuery<Actor>(ds, "select id, first_name, last_name from t_actor where id = ?") {
@@ -79,6 +84,7 @@ data from the `t_actor` relation to an instance of the `Actor` class:
}
----
======
The class extends `MappingSqlQuery` parameterized with the `Actor` type. The constructor
for this customer query takes a `DataSource` as the only parameter. In this
@@ -93,8 +99,11 @@ thread-safe after it is compiled, so, as long as these instances are created whe
is initialized, they can be kept as instance variables and be reused. The following
example shows how to define such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private ActorMappingQuery actorMappingQuery;
@@ -107,13 +116,16 @@ example shows how to define such a class:
return actorMappingQuery.findObject(id);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getCustomer(id: Long) = actorMappingQuery.findObject(id)
----
======
The method in the preceding example retrieves the customer with the `id` that is passed in as the
only parameter. Since we want only one object to be returned, we call the `findObject` convenience
@@ -122,19 +134,25 @@ list of objects and took additional parameters, we would use one of the `execute
methods that takes an array of parameter values passed in as varargs. The following
example shows such a method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public List<Actor> searchForActors(int age, String namePattern) {
return actorSearchMappingQuery.execute(age, namePattern);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun searchForActors(age: Int, namePattern: String) =
actorSearchMappingQuery.execute(age, namePattern)
----
======
[[jdbc-SqlUpdate]]
@@ -149,8 +167,11 @@ However, you do not have to subclass the `SqlUpdate`
class, since it can easily be parameterized by setting SQL and declaring parameters.
The following example creates a custom update method named `execute`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.Types;
import javax.sql.DataSource;
@@ -177,8 +198,10 @@ The following example creates a custom update method named `execute`:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.Types
import javax.sql.DataSource
@@ -205,6 +228,7 @@ The following example creates a custom update method named `execute`:
}
}
----
======
[[jdbc-StoredProcedure]]
@@ -219,18 +243,24 @@ To define a parameter for the `StoredProcedure` class, you can use an `SqlParame
of its subclasses. You must specify the parameter name and SQL type in the constructor,
as the following code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR),
----
======
The SQL type is specified using the `java.sql.Types` constants.
@@ -259,8 +289,11 @@ returned date from the results `Map`. The results `Map` has an entry for each de
output parameter (in this case, only one) by using the parameter name as the key.
The following listing shows our custom StoredProcedure class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.Types;
import java.util.Date;
@@ -306,8 +339,10 @@ The following listing shows our custom StoredProcedure class:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.Types
import java.util.Date
@@ -343,12 +378,16 @@ The following listing shows our custom StoredProcedure class:
}
}
----
======
The following example of a `StoredProcedure` has two output parameters (in this case,
Oracle REF cursors):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.util.HashMap;
import java.util.Map;
@@ -374,8 +413,10 @@ Oracle REF cursors):
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.util.HashMap
import javax.sql.DataSource
@@ -401,6 +442,7 @@ Oracle REF cursors):
}
}
----
======
Notice how the overloaded variants of the `declareParameter(..)` method that have been
used in the `TitlesAndGenresStoredProcedure` constructor are passed `RowMapper`
@@ -410,8 +452,11 @@ functionality. The next two examples provide code for the two `RowMapper` implem
The `TitleMapper` class maps a `ResultSet` to a `Title` domain object for each row in
the supplied `ResultSet`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -428,8 +473,10 @@ the supplied `ResultSet`, as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.ResultSet
import com.foo.domain.Title
@@ -441,12 +488,16 @@ the supplied `ResultSet`, as follows:
Title(rs.getLong("id"), rs.getString("name"))
}
----
======
The `GenreMapper` class maps a `ResultSet` to a `Genre` domain object for each row in
the supplied `ResultSet`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -460,8 +511,10 @@ the supplied `ResultSet`, as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.ResultSet
import com.foo.domain.Genre
@@ -474,13 +527,17 @@ the supplied `ResultSet`, as follows:
}
}
----
======
To pass parameters to a stored procedure that has one or more input parameters in its
definition in the RDBMS, you can code a strongly typed `execute(..)` method that would
delegate to the untyped `execute(Map)` method in the superclass, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.Types;
import java.util.Date;
@@ -511,8 +568,10 @@ delegate to the untyped `execute(Map)` method in the superclass, as the followin
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.Types
import java.util.Date
@@ -539,6 +598,7 @@ delegate to the untyped `execute(Map)` method in the superclass, as the followin
mapOf<String, Any>(CUTOFF_DATE_PARAM to cutoffDate))
}
----
======

View File

@@ -63,8 +63,11 @@ dependency injection.
The following example shows how to create and insert a BLOB:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final File blobIn = new File("spring2004.jpg");
final InputStream blobIs = new FileInputStream(blobIn);
@@ -86,6 +89,7 @@ The following example shows how to create and insert a BLOB:
blobIs.close();
clobReader.close();
----
======
<1> Pass in the `lobHandler` that (in this example) is a plain `DefaultLobHandler`.
<2> Using the method `setClobAsCharacterStream` to pass in the contents of the CLOB.
<3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
@@ -134,8 +138,11 @@ Now it is time to read the LOB data from the database. Again, you use a `JdbcTem
with the same instance variable `lobHandler` and a reference to a `DefaultLobHandler`.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
new RowMapper<Map<String, Object>>() {
@@ -149,6 +156,7 @@ The following example shows how to do so:
}
});
----
======
<1> Using the method `getClobAsString` to retrieve the contents of the CLOB.
<2> Using the method `getBlobAsBytes` to retrieve the contents of the BLOB.
@@ -203,8 +211,11 @@ implemented. This interface is used as part of the declaration of an `SqlOutPara
The following example shows returning the value of an Oracle `STRUCT` object of the user
declared type `ITEM_TYPE`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TestItemStoredProcedure extends StoredProcedure {
@@ -223,8 +234,10 @@ declared type `ITEM_TYPE`:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
@@ -239,6 +252,7 @@ declared type `ITEM_TYPE`:
}
}
----
======
You can use `SqlTypeValue` to pass the value of a Java object (such as `TestItem`) to a
stored procedure. The `SqlTypeValue` interface has a single method (named
@@ -246,8 +260,11 @@ stored procedure. The `SqlTypeValue` interface has a single method (named
can use it to create database-specific objects, such as `StructDescriptor` instances
or `ArrayDescriptor` instances. The following example creates a `StructDescriptor` instance:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final TestItem testItem = new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
@@ -265,8 +282,10 @@ or `ArrayDescriptor` instances. The following example creates a `StructDescripto
}
};
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val (id, description, expirationDate) = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
@@ -279,6 +298,7 @@ or `ArrayDescriptor` instances. The following example creates a `StructDescripto
}
}
----
======
You can now add this `SqlTypeValue` to the `Map` that contains the input parameters for the
`execute` call of the stored procedure.
@@ -288,8 +308,11 @@ procedure. Oracle has its own internal `ARRAY` class that must be used in this c
you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate
it with values from the Java `ARRAY`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final Long[] ids = new Long[] {1L, 2L};
@@ -301,8 +324,10 @@ it with values from the Java `ARRAY`, as the following example shows:
}
};
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
@@ -317,6 +342,7 @@ it with values from the Java `ARRAY`, as the following example shows:
}
}
----
======

View File

@@ -19,8 +19,11 @@ Configuration methods for this class follow the `fluid` style that returns the i
of the `SimpleJdbcInsert`, which lets you chain all configuration methods. The following
example uses only one configuration method (we show examples of multiple methods later):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -41,8 +44,10 @@ example uses only one configuration method (we show examples of multiple methods
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -59,6 +64,7 @@ example uses only one configuration method (we show examples of multiple methods
// ... additional methods
}
----
======
The `execute` method used here takes a plain `java.util.Map` as its only parameter. The
important thing to note here is that the keys used for the `Map` must match the column
@@ -75,8 +81,11 @@ the `SimpleJdbcInsert`, in addition to specifying the table name, it specifies t
of the generated key column with the `usingGeneratedKeyColumns` method. The following
listing shows how it works:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -99,8 +108,10 @@ listing shows how it works:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -118,6 +129,7 @@ listing shows how it works:
// ... additional methods
}
----
======
The main difference when you run the insert by using this second approach is that you do not
add the `id` to the `Map`, and you call the `executeAndReturnKey` method. This returns a
@@ -134,8 +146,11 @@ use a `KeyHolder` that is returned from the `executeAndReturnKeyHolder` method.
You can limit the columns for an insert by specifying a list of column names with the
`usingColumns` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -159,8 +174,10 @@ You can limit the columns for an insert by specifying a list of column names wit
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -180,6 +197,7 @@ You can limit the columns for an insert by specifying a list of column names wit
// ... additional methods
}
----
======
The execution of the insert is the same as if you had relied on the metadata to determine
which columns to use.
@@ -195,8 +213,11 @@ which is a very convenient class if you have a JavaBean-compliant class that con
your values. It uses the corresponding getter method to extract the parameter
values. The following example shows how to use `BeanPropertySqlParameterSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -217,8 +238,10 @@ values. The following example shows how to use `BeanPropertySqlParameterSource`:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -235,12 +258,16 @@ values. The following example shows how to use `BeanPropertySqlParameterSource`:
// ... additional methods
}
----
======
Another option is the `MapSqlParameterSource` that resembles a `Map` but provides a more
convenient `addValue` method that can be chained. The following example shows how to use it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -263,8 +290,10 @@ convenient `addValue` method that can be chained. The following example shows ho
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -283,6 +312,7 @@ convenient `addValue` method that can be chained. The following example shows ho
// ... additional methods
}
----
======
As you can see, the configuration is the same. Only the executing code has to change to
use these alternative input classes.
@@ -325,8 +355,11 @@ The following example of a `SimpleJdbcCall` configuration uses the preceding sto
procedure (the only configuration option, in addition to the `DataSource`, is the name
of the stored procedure):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -352,8 +385,10 @@ of the stored procedure):
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -374,6 +409,7 @@ of the stored procedure):
// ... additional methods
}
----
======
The code you write for the execution of the call involves creating an `SqlParameterSource`
containing the IN parameter. You must match the name provided for the input value
@@ -397,8 +433,11 @@ To do the latter, you can create your own `JdbcTemplate` and set the `setResults
property to `true`. Then you can pass this customized `JdbcTemplate` instance into
the constructor of your `SimpleJdbcCall`. The following example shows this configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -414,8 +453,10 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -426,6 +467,7 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
// ... additional methods
}
----
======
By taking this action, you avoid conflicts in the case used for the names of your
returned `out` parameters.
@@ -456,8 +498,11 @@ of IN parameter names to include for a given signature.
The following example shows a fully declared procedure call and uses the information from
the preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -481,8 +526,10 @@ the preceding example:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -501,6 +548,7 @@ the preceding example:
// ... additional methods
}
----
======
The execution and end results of the two examples are the same. The second example specifies all
details explicitly rather than relying on metadata.
@@ -515,18 +563,24 @@ To do so, you typically specify the parameter name and SQL type in the construct
is specified by using the `java.sql.Types` constants. Earlier in this chapter, we saw declarations
similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR),
----
======
The first line with the `SqlParameter` declares an IN parameter. You can use IN parameters
for both stored procedure calls and for queries by using the `SqlQuery` and its
@@ -578,8 +632,11 @@ that returns an actor's full name:
To call this function, we again create a `SimpleJdbcCall` in the initialization method,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -602,8 +659,10 @@ as the following example shows:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -621,6 +680,7 @@ as the following example shows:
// ... additional methods
}
----
======
The `executeFunction` method used returns a `String` that contains the return value from the
function call.
@@ -656,8 +716,11 @@ to map follows the JavaBean rules, you can use a `BeanPropertyRowMapper` that is
passing in the required class to map to in the `newInstance` method.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@@ -680,8 +743,10 @@ The following example shows how to do so:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@@ -699,6 +764,7 @@ The following example shows how to do so:
// ... additional methods
}
----
======
The `execute` call passes in an empty `Map`, because this call does not take any parameters.
The list of actors is then retrieved from the results map and returned to the caller.

View File

@@ -61,8 +61,11 @@ do not need any special exception treatment (or both). However, Spring lets exce
translation be applied transparently through the `@Repository` annotation. The following
examples (one for Java configuration and one for XML configuration) show how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class ProductDaoImpl implements ProductDao {
@@ -71,8 +74,10 @@ examples (one for Java configuration and one for XML configuration) show how to
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class ProductDaoImpl : ProductDao {
@@ -81,6 +86,7 @@ examples (one for Java configuration and one for XML configuration) show how to
}
----
======
[source,xml,indent=0,subs="verbatim,quotes"]
----

View File

@@ -95,8 +95,11 @@ one current `Session` per transaction. This is roughly equivalent to Spring's
synchronization of one Hibernate `Session` per transaction. A corresponding DAO
implementation resembles the following example, based on the plain Hibernate API:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ProductDaoImpl implements ProductDao {
@@ -114,8 +117,10 @@ implementation resembles the following example, based on the plain Hibernate API
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao {
@@ -127,6 +132,7 @@ implementation resembles the following example, based on the plain Hibernate API
}
}
----
======
This style is similar to that of the Hibernate reference documentation and examples,
except for holding the `SessionFactory` in an instance variable. We strongly recommend
@@ -192,8 +198,11 @@ You can annotate the service layer with `@Transactional` annotations and instruc
Spring container to find these annotations and provide transactional semantics for
these annotated methods. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ProductServiceImpl implements ProductService {
@@ -215,8 +224,10 @@ these annotated methods. The following example shows how to do so:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ProductServiceImpl(private val productDao: ProductDao) : ProductService {
@@ -230,6 +241,7 @@ these annotated methods. The following example shows how to do so:
fun findAllProducts() = productDao.findAllProducts()
}
----
======
In the container, you need to set up the `PlatformTransactionManager` implementation
(as a bean) and a `<tx:annotation-driven/>` entry, opting into `@Transactional`
@@ -295,8 +307,11 @@ and an example for a business method implementation:
</beans>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ProductServiceImpl implements ProductService {
@@ -321,8 +336,10 @@ and an example for a business method implementation:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ProductServiceImpl(transactionManager: PlatformTransactionManager,
private val productDao: ProductDao) : ProductService {
@@ -337,6 +354,7 @@ and an example for a business method implementation:
}
}
----
======
Spring's `TransactionInterceptor` lets any checked application exception be thrown
with the callback code, while `TransactionTemplate` is restricted to unchecked

View File

@@ -293,8 +293,11 @@ using an injected `EntityManagerFactory` or `EntityManager`. Spring can understa
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain JPA DAO implementation
that uses the `@PersistenceUnit` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ProductDaoImpl implements ProductDao {
@@ -320,8 +323,10 @@ that uses the `@PersistenceUnit` annotation:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ProductDaoImpl : ProductDao {
@@ -340,6 +345,7 @@ that uses the `@PersistenceUnit` annotation:
}
}
----
======
The preceding DAO has no dependency on Spring and still fits nicely into a Spring
application context. Moreover, the DAO takes advantage of annotations to require the
@@ -382,8 +388,11 @@ the factory. You can avoid this by requesting a transactional `EntityManager` (a
called a "`shared EntityManager`" because it is a shared, thread-safe proxy for the actual
transactional EntityManager) to be injected instead of the factory. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ProductDaoImpl implements ProductDao {
@@ -397,8 +406,10 @@ transactional EntityManager) to be injected instead of the factory. The followin
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ProductDaoImpl : ProductDao {
@@ -412,6 +423,7 @@ transactional EntityManager) to be injected instead of the factory. The followin
}
}
----
======
The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults to
`PersistenceContextType.TRANSACTION`. You can use this default to receive a shared

View File

@@ -171,8 +171,11 @@ You can use Spring's OXM for a wide variety of situations. In the following exam
use it to marshal the settings of a Spring-managed application as an XML file. In the following example, we
use a simple JavaBean to represent the settings:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Settings {
@@ -187,21 +190,27 @@ use a simple JavaBean to represent the settings:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Settings {
var isFooEnabled: Boolean = false
}
----
======
The application class uses this bean to store its settings. Besides a main method, the
class has two methods: `saveSettings()` saves the settings bean to a file named
`settings.xml`, and `loadSettings()` loads these settings again. The following `main()` method
constructs a Spring application context and calls these two methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -249,8 +258,10 @@ constructs a Spring application context and calls these two methods:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Application {
@@ -276,6 +287,7 @@ constructs a Spring application context and calls these two methods:
application.loadSettings()
}
----
======
The `Application` requires both a `marshaller` and an `unmarshaller` property to be set. We
can do so by using the following `applicationContext.xml`:

View File

@@ -60,16 +60,22 @@ and give it to DAOs as a bean reference.
The simplest way to create a `DatabaseClient` object is through a static factory method, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DatabaseClient client = DatabaseClient.create(connectionFactory);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = DatabaseClient.create(connectionFactory)
----
======
NOTE: The `ConnectionFactory` should always be configured as a bean in the Spring IoC
container.
@@ -119,18 +125,24 @@ See the attendant {api-spring-framework}/r2dbc/core/DatabaseClient.html[javadoc]
The following example shows what you need to include for minimal but fully functional
code that creates a new table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.then();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.await()
----
======
`DatabaseClient` is designed for convenient, fluent usage.
It exposes intermediate, continuation, and terminal methods at each stage of the
@@ -150,35 +162,47 @@ depending on the issued query.
The following query gets the `id` and `name` columns from a table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person")
.fetch().first();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val first = client.sql("SELECT id, name FROM person")
.fetch().awaitSingle()
----
======
The following query uses a bind variable:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe")
.fetch().first();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe")
.fetch().awaitSingle()
----
======
You might have noticed the use of `fetch()` in the example above. `fetch()` is a
continuation operator that lets you specify how much data you want to consume.
@@ -205,20 +229,26 @@ collections and maps, and objects).
The following example extracts the `name` column and emits its value:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Flux<String> names = client.sql("SELECT name FROM person")
.map(row -> row.get("name", String.class))
.all();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val names = client.sql("SELECT name FROM person")
.map{ row: Row -> row.get("name", String.class) }
.flow()
----
======
[[r2dbc-DatabaseClient-mapping-null]]
@@ -242,20 +272,26 @@ do not return tabular data so you use `rowsUpdated()` to consume results.
The following example shows an `UPDATE` statement that returns the number
of updated rows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().rowsUpdated();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().awaitRowsUpdated()
----
======
[[r2dbc-DatabaseClient-named-parameters]]
==== Binding Values to Queries
@@ -277,7 +313,6 @@ Parameter binding supports two binding strategies:
The following example shows parameter binding for a query:
====
[source,java]
----
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
@@ -285,7 +320,6 @@ db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("name", "Joe")
.bind("age", 34);
----
====
.R2DBC Native Bind Markers
****
@@ -318,8 +352,11 @@ SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50
The preceding query can be parameterized and run as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
List<Object[]> tuples = new ArrayList<>();
tuples.add(new Object[] {"John", 35});
@@ -328,8 +365,10 @@ The preceding query can be parameterized and run as follows:
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35))
@@ -338,19 +377,25 @@ The preceding query can be parameterized and run as follows:
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples)
----
======
NOTE: Usage of select lists is vendor-dependent.
The following example shows a simpler variant using `IN` predicates:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", Arrays.asList(35, 50));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35))
@@ -359,6 +404,7 @@ The following example shows a simpler variant using `IN` predicates:
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("tuples", arrayOf(35, 50))
----
======
NOTE: R2DBC itself does not support Collection-like values. Nevertheless,
expanding a given `List` in the example above works for named parameters
@@ -376,27 +422,36 @@ before it gets run. Register a `Statement` filter
(`StatementFilterFunction`) through `DatabaseClient` to intercept and
modify statements in their execution, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …)
.bind("state", …);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
.bind("name", …)
.bind("state", …)
----
======
`DatabaseClient` exposes also simplified `filter(…)` overload accepting `Function<Statement, Statement>`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"));
@@ -404,8 +459,10 @@ modify statements in their execution, as the following example shows:
client.sql("SELECT id, name, state FROM table")
.filter(statement -> s.fetchSize(25));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") }
@@ -413,6 +470,7 @@ modify statements in their execution, as the following example shows:
client.sql("SELECT id, name, state FROM table")
.filter { statement -> s.fetchSize(25) }
----
======
`StatementFilterFunction` implementations allow filtering of the
`Statement` and filtering of `Result` objects.
@@ -432,8 +490,11 @@ that shared `ConnectionFactory` bean into your DAO classes. The `DatabaseClient`
the setter for the `ConnectionFactory`. This leads to DAOs that resemble the following:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class R2dbcCorporateEventDao implements CorporateEventDao {
@@ -446,8 +507,10 @@ the setter for the `ConnectionFactory`. This leads to DAOs that resemble the fol
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao {
@@ -456,6 +519,7 @@ the setter for the `ConnectionFactory`. This leads to DAOs that resemble the fol
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
--
An alternative to explicit configuration is to use component-scanning and annotation
@@ -464,8 +528,11 @@ support for dependency injection. In this case, you can annotate the class with
method with `@Autowired`. The following example shows how to do so:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component // <1>
public class R2dbcCorporateEventDao implements CorporateEventDao {
@@ -480,6 +547,7 @@ method with `@Autowired`. The following example shows how to do so:
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
<1> Annotate the class with `@Component`.
<2> Annotate the `ConnectionFactory` setter method with `@Autowired`.
<3> Create a new `DatabaseClient` with the `ConnectionFactory`.
@@ -516,8 +584,11 @@ that defines an auto-increment or identity column. To get full control over
the column name to generate, simply register a `StatementFilterFunction` that
requests the generated key for the desired column.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"))
@@ -526,8 +597,10 @@ requests the generated key for the desired column.
// generatedId emits the generated key once the INSERT statement has finished
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") }
@@ -536,6 +609,7 @@ requests the generated key for the desired column.
// generatedId emits the generated key once the INSERT statement has finished
----
======
[[r2dbc-connections]]
@@ -575,16 +649,22 @@ To configure a `ConnectionFactory`:
The following example shows how to configure a `ConnectionFactory`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
----
======
[[r2dbc-ConnectionFactoryUtils]]

View File

@@ -15,8 +15,11 @@ The ease-of-use afforded by the use of the `@Transactional` annotation is best
illustrated with an example, which is explained in the text that follows.
Consider the following class definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// the service class that we want to make transactional
@Transactional
@@ -43,8 +46,10 @@ Consider the following class definition:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// the service class that we want to make transactional
@Transactional
@@ -67,6 +72,7 @@ Consider the following class definition:
}
}
----
======
Used at the class level as above, the annotation indicates a default for all methods of
the declaring class (as well as its subclasses). Alternatively, each method can be
@@ -128,8 +134,11 @@ preceding example.
Reactive transactional methods use reactive return types in contrast to imperative
programming arrangements as the following listing shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// the reactive service class that we want to make transactional
@Transactional
@@ -156,8 +165,10 @@ programming arrangements as the following listing shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// the reactive service class that we want to make transactional
@Transactional
@@ -180,6 +191,7 @@ programming arrangements as the following listing shows:
}
}
----
======
Note that there are special considerations for the returned `Publisher` with regards to
Reactive Streams cancellation signals. See the xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals] section under
@@ -322,8 +334,11 @@ annotated at the class level with the settings for a read-only transaction, but
`@Transactional` annotation on the `updateFoo(Foo)` method in the same class takes
precedence over the transactional settings defined at the class level.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
@@ -339,8 +354,10 @@ precedence over the transactional settings defined at the class level.
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Transactional(readOnly = true)
class DefaultFooService : FooService {
@@ -356,6 +373,7 @@ precedence over the transactional settings defined at the class level.
}
}
----
======
[[transaction-declarative-attransactional-settings]]
@@ -455,8 +473,11 @@ of the transaction manager bean. For example, using the qualifier notation, you
combine the following Java code with the following transaction manager bean declarations
in the application context:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TransactionalService {
@@ -470,8 +491,10 @@ in the application context:
public Mono<Void> doSomethingReactive() { ... }
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
class TransactionalService {
@@ -491,6 +514,7 @@ in the application context:
}
}
----
======
The following listing shows the bean declarations:
@@ -527,8 +551,11 @@ methods, xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[Spring's
define custom composed annotations for your specific use cases. For example, consider the
following annotation definitions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -542,8 +569,10 @@ following annotation definitions:
public @interface AccountTx {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@@ -555,11 +584,15 @@ following annotation definitions:
@Transactional(transactionManager = "account", label = ["retryable"])
annotation class AccountTx
----
======
The preceding annotations let us write the example from the previous section as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TransactionalService {
@@ -574,8 +607,10 @@ The preceding annotations let us write the example from the previous section as
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
class TransactionalService {
@@ -590,6 +625,7 @@ The preceding annotations let us write the example from the previous section as
}
}
----
======
In the preceding example, we used the syntax to define the transaction manager qualifier
and transactional labels, but we could also have included propagation behavior,

View File

@@ -18,8 +18,11 @@ configuration and AOP in general.
The following code shows the simple profiling aspect discussed earlier:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y;
@@ -55,8 +58,10 @@ The following code shows the simple profiling aspect discussed earlier:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y
@@ -92,6 +97,7 @@ The following code shows the simple profiling aspect discussed earlier:
}
}
----
======
The ordering of advice
is controlled through the `Ordered` interface. For full details on advice ordering, see

View File

@@ -20,8 +20,11 @@ xref:core/aop.adoc[AOP] respectively.
The following example shows how to create a transaction manager and configure the
`AnnotationTransactionAspect` to use it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// construct an appropriate transaction manager
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());
@@ -29,8 +32,10 @@ The following example shows how to create a transaction manager and configure th
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// construct an appropriate transaction manager
val txManager = DataSourceTransactionManager(getDataSource())
@@ -38,6 +43,7 @@ The following example shows how to create a transaction manager and configure th
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().transactionManager = txManager
----
======
NOTE: When you use this aspect, you must annotate the implementation class (or the methods
within that class or both), not the interface (if any) that the class implements. AspectJ

View File

@@ -10,8 +10,11 @@ transactions being created and then rolled back in response to the
`UnsupportedOperationException` instance. The following listing shows the `FooService`
interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// the service interface that we want to make transactional
@@ -29,8 +32,10 @@ interface:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
// the service interface that we want to make transactional
@@ -47,11 +52,15 @@ interface:
fun updateFoo(foo: Foo)
}
----
======
The following example shows an implementation of the preceding interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y.service;
@@ -78,8 +87,10 @@ The following example shows an implementation of the preceding interface:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y.service
@@ -102,6 +113,7 @@ The following example shows an implementation of the preceding interface:
}
}
----
======
Assume that the first two methods of the `FooService` interface, `getFoo(String)` and
`getFoo(String, String)`, must run in the context of a transaction with read-only
@@ -215,8 +227,11 @@ a transaction is started, suspended, marked as read-only, and so on, depending o
transaction configuration associated with that method. Consider the following program
that test drives the configuration shown earlier:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public final class Boot {
@@ -227,8 +242,10 @@ that test drives the configuration shown earlier:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@@ -238,6 +255,7 @@ that test drives the configuration shown earlier:
fooService.insertFoo(Foo())
}
----
======
The output from running the preceding program should resemble the following (the Log4J
output and the stack trace from the `UnsupportedOperationException` thrown by the
@@ -281,8 +299,11 @@ return type is reactive.
The following listing shows a modified version of the previously used `FooService`, but
this time the code uses reactive types:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// the reactive service interface that we want to make transactional
@@ -300,8 +321,10 @@ this time the code uses reactive types:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
// the reactive service interface that we want to make transactional
@@ -318,11 +341,15 @@ this time the code uses reactive types:
fun updateFoo(foo: Foo) : Mono<Void>
}
----
======
The following example shows an implementation of the preceding interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y.service;
@@ -349,8 +376,10 @@ The following example shows an implementation of the preceding interface:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y.service
@@ -373,6 +402,7 @@ The following example shows an implementation of the preceding interface:
}
}
----
======
Imperative and reactive transaction management share the same semantics for transaction
boundary and transaction attribute definitions. The main difference between imperative

View File

@@ -26,8 +26,11 @@ automatically rolled back in case of a failure. For more information on Vavr's T
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
Here's an example of how to use Vavr's Try with a transactional method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Transactional
public Try<String> myTransactionalMethod() {
@@ -37,6 +40,7 @@ Here's an example of how to use Vavr's Try with a transactional method:
return Try.of(delegate::myDataAccessOperation);
}
----
======
Checked exceptions that are thrown from a transactional method do not result in a rollback
in the default configuration. You can configure exactly which `Exception` types mark a
@@ -138,8 +142,11 @@ is quite invasive and tightly couples your code to the Spring Framework's transa
infrastructure. The following example shows how to programmatically indicate a required
rollback:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public void resolvePosition() {
try {
@@ -150,8 +157,10 @@ rollback:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun resolvePosition() {
try {
@@ -162,6 +171,7 @@ rollback:
}
}
----
======
You are strongly encouraged to use the declarative approach to rollback, if at all
possible. Programmatic rollback is available should you absolutely need it, but its

View File

@@ -15,8 +15,11 @@ event and that we want to define a listener that should only handle that event o
transaction in which it has been published has committed successfully. The following
example sets up such an event listener:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MyComponent {
@@ -27,8 +30,10 @@ example sets up such an event listener:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
class MyComponent {
@@ -39,6 +44,7 @@ example sets up such an event listener:
}
}
----
======
The `@TransactionalEventListener` annotation exposes a `phase` attribute that lets you
customize the phase of the transaction to which the listener should be bound.

View File

@@ -33,8 +33,11 @@ anonymous inner class) that contains the code that you need to run in the contex
a transaction. You can then pass an instance of your custom `TransactionCallback` to the
`execute(..)` method exposed on the `TransactionTemplate`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleService implements Service {
@@ -57,8 +60,10 @@ a transaction. You can then pass an instance of your custom `TransactionCallback
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// use constructor-injection to supply the PlatformTransactionManager
class SimpleService(transactionManager: PlatformTransactionManager) : Service {
@@ -72,13 +77,17 @@ a transaction. You can then pass an instance of your custom `TransactionCallback
}
}
----
======
If there is no return value, you can use the convenient `TransactionCallbackWithoutResult` class
with an anonymous class, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
@@ -87,8 +96,10 @@ with an anonymous class, as follows:
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) {
@@ -97,13 +108,17 @@ with an anonymous class, as follows:
}
})
----
======
Code within the callback can roll the transaction back by calling the
`setRollbackOnly()` method on the supplied `TransactionStatus` object, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@@ -117,8 +132,10 @@ Code within the callback can roll the transaction back by calling the
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
@@ -132,6 +149,7 @@ Code within the callback can roll the transaction back by calling the
}
})
----
======
[[tx-prog-template-settings]]
=== Specifying Transaction Settings
@@ -143,8 +161,11 @@ xref:data-access/transaction/declarative/txadvice-settings.adoc[default transact
following example shows the programmatic customization of the transactional settings for
a specific `TransactionTemplate:`
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleService implements Service {
@@ -160,8 +181,10 @@ a specific `TransactionTemplate:`
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleService(transactionManager: PlatformTransactionManager) : Service {
@@ -173,6 +196,7 @@ a specific `TransactionTemplate:`
}
}
----
======
The following example defines a `TransactionTemplate` with some custom transactional
settings by using Spring XML configuration:
@@ -212,8 +236,11 @@ to make yourself.
Application code that must run in a transactional context and that explicitly uses
the `TransactionalOperator` resembles the next example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleService implements Service {
@@ -235,8 +262,10 @@ the `TransactionalOperator` resembles the next example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// use constructor-injection to supply the ReactiveTransactionManager
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
@@ -250,6 +279,7 @@ the `TransactionalOperator` resembles the next example:
}
}
----
======
`TransactionalOperator` can be used in two ways:
@@ -259,8 +289,11 @@ the `TransactionalOperator` resembles the next example:
Code within the callback can roll the transaction back by calling the `setRollbackOnly()`
method on the supplied `ReactiveTransaction` object, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
transactionalOperator.execute(new TransactionCallback<>() {
@@ -271,8 +304,10 @@ method on the supplied `ReactiveTransaction` object, as follows:
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
transactionalOperator.execute(object : TransactionCallback() {
@@ -282,6 +317,7 @@ method on the supplied `ReactiveTransaction` object, as follows:
}
})
----
======
[[tx-prog-operator-cancel]]
=== Cancel Signals
@@ -306,8 +342,11 @@ xref:data-access/transaction/declarative/txadvice-settings.adoc[default transact
following example shows customization of the transactional settings for a specific
`TransactionalOperator:`
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleService implements Service {
@@ -325,8 +364,10 @@ following example shows customization of the transactional settings for a specif
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
@@ -339,6 +380,7 @@ following example shows customization of the transactional settings for a specif
private val transactionalOperator = TransactionalOperator(transactionManager, definition)
}
----
======
[[transaction-programmatic-tm]]
== Using the `TransactionManager`
@@ -356,8 +398,11 @@ use to your bean through a bean reference. Then, by using the `TransactionDefini
`TransactionStatus` objects, you can initiate transactions, roll back, and commit. The
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
@@ -373,8 +418,10 @@ following example shows how to do so:
}
txManager.commit(status);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically
@@ -391,6 +438,7 @@ following example shows how to do so:
txManager.commit(status)
----
======
[[transaction-programmatic-rtm]]
@@ -403,8 +451,11 @@ use to your bean through a bean reference. Then, by using the `TransactionDefini
`ReactiveTransaction` objects, you can initiate transactions, roll back, and commit. The
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
@@ -421,8 +472,10 @@ following example shows how to do so:
.onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex)));
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically
@@ -438,5 +491,6 @@ following example shows how to do so:
.onErrorResume { ex -> txManager.rollback(status).then(Mono.error(ex)) }
}
----
======