Migrate to Asciidoctor Tabs
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"]
|
||||
----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`:
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)) }
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user