提交 daee6eea 编写于 作者: J Juergen Hoeller

ParameterMetaData.getParameterType performance on Oracle 12c

Issue: SPR-16139
上级 a8b48848
...@@ -160,8 +160,7 @@ strategy__. A transaction strategy is defined by the ...@@ -160,8 +160,7 @@ strategy__. A transaction strategy is defined by the
---- ----
public interface PlatformTransactionManager { public interface PlatformTransactionManager {
TransactionStatus getTransaction( TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException; void commit(TransactionStatus status) throws TransactionException;
...@@ -2990,10 +2989,10 @@ An `update()` convenience method supports the retrieval of primary keys generate ...@@ -2990,10 +2989,10 @@ An `update()` convenience method supports the retrieval of primary keys generate
database. This support is part of the JDBC 3.0 standard; see Chapter 13.6 of the database. This support is part of the JDBC 3.0 standard; see Chapter 13.6 of the
specification for details. The method takes a `PreparedStatementCreator` as its first specification for details. The method takes a `PreparedStatementCreator` as its first
argument, and this is the way the required insert statement is specified. The other argument, and this is the way the required insert statement is specified. The other
argument is a `KeyHolder`, which contains the generated key on successful return from argument is a `KeyHolder`, which contains the generated key on successful return from the
the update. There is not a standard single way to create an appropriate update. There is not a standard single way to create an appropriate `PreparedStatement`
`PreparedStatement` (which explains why the method signature is the way it is). The (which explains why the method signature is the way it is). The following example works
following example works on Oracle but may not work on other platforms: on Oracle but may not work on other platforms:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
...@@ -3244,6 +3243,7 @@ based on entries in a list. The entire list is used as the batch in this example ...@@ -3244,6 +3243,7 @@ based on entries in a list. The entire list is used as the batch in this example
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) { public void setDataSource(DataSource dataSource) {
...@@ -3251,20 +3251,18 @@ based on entries in a list. The entire list is used as the batch in this example ...@@ -3251,20 +3251,18 @@ based on entries in a list. The entire list is used as the batch in this example
} }
public int[] batchUpdate(final List<Actor> actors) { public int[] batchUpdate(final List<Actor> actors) {
int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, " + return this.jdbcTemplate.batchUpdate(
"last_name = ? where id = ?", "update t_actor set first_name = ?, last_name = ? where id = ?",
new BatchPreparedStatementSetter() { new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException { public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, actors.get(i).getFirstName()); ps.setString(1, actors.get(i).getFirstName());
ps.setString(2, actors.get(i).getLastName()); ps.setString(2, actors.get(i).getLastName());
ps.setLong(3, actors.get(i).getId().longValue()); ps.setLong(3, actors.get(i).getId().longValue());
} }
public int getBatchSize() { public int getBatchSize() {
return actors.size(); return actors.size();
} }
}); });
return updateCounts;
} }
// ... additional methods // ... additional methods
...@@ -3287,8 +3285,9 @@ provide all parameter values in the call as a list. The framework loops over the ...@@ -3287,8 +3285,9 @@ provide all parameter values in the call as a list. The framework loops over the
values and uses an internal prepared statement setter. The API varies depending on values and uses an internal prepared statement setter. The API varies depending on
whether you use named parameters. For the named parameters you provide an array of whether you use named parameters. For the named parameters you provide an array of
`SqlParameterSource`, one entry for each member of the batch. You can use the `SqlParameterSource`, one entry for each member of the batch. You can use the
`SqlParameterSource.createBatch` method to create this array, passing in either an array `SqlParameterSourceUtils.createBatch` convenience methods to create this array, passing
of JavaBeans or an array of Maps containing the parameter values. in an array of bean-style objects (with getter methods corresponding to parameters)
and/or String-keyed Maps (containing the corresponding parameters as values).
This example shows a batch update using named parameters: This example shows a batch update using named parameters:
...@@ -3296,18 +3295,17 @@ This example shows a batch update using named parameters: ...@@ -3296,18 +3295,17 @@ This example shows a batch update using named parameters:
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
private NamedParameterTemplate namedParameterJdbcTemplate; private NamedParameterTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) { public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
} }
public int[] batchUpdate(final List<Actor> actors) { public int[] batchUpdate(List<Actor> actors) {
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray()); return this.namedParameterJdbcTemplate.batchUpdate(
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id", "update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
batch); SqlParameterSourceUtils.createBatch(actors));
return updateCounts;
} }
// ... additional methods // ... additional methods
...@@ -3336,15 +3334,12 @@ The same example using classic JDBC "?" placeholders: ...@@ -3336,15 +3334,12 @@ The same example using classic JDBC "?" placeholders:
List<Object[]> batch = new ArrayList<Object[]>(); List<Object[]> batch = new ArrayList<Object[]>();
for (Actor actor : actors) { for (Actor actor : actors) {
Object[] values = new Object[] { Object[] values = new Object[] {
actor.getFirstName(), actor.getFirstName(), actor.getLastName(), actor.getId()};
actor.getLastName(),
actor.getId()};
batch.add(values); batch.add(values);
} }
int[] updateCounts = jdbcTemplate.batchUpdate( return this.jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?", "update t_actor set first_name = ?, last_name = ? where id = ?",
batch); batch);
return updateCounts;
} }
// ... additional methods // ... additional methods
...@@ -3355,6 +3350,24 @@ All of the above batch update methods return an int array containing the number ...@@ -3355,6 +3350,24 @@ All of the above batch update methods return an int array containing the number
affected rows for each batch entry. This count is reported by the JDBC driver. If the affected rows for each batch entry. This count is reported by the JDBC driver. If the
count is not available, the JDBC driver returns a -2 value. count is not available, the JDBC driver returns a -2 value.
[NOTE]
====
In such a scenario with automatic setting of values on an underlying `PreparedStatement`,
the corresponding JDBC type for each value needs to be derived from the given Java type.
While this usually works well, there is a potential for issues, e.g. with Map-contained
`null` values: Spring will by default call `ParameterMetaData.getParameterType` in such a
case which may be expensive with your JDBC driver. Please make sure to use a recent driver
version, and consider setting the "spring.jdbc.getParameterType.ignore" property to "true"
(as a JVM system property or in a `spring.properties` file in the root of your classpath)
if you encounter a performance issue, e.g. as reported on Oracle 12c (SPR-16139).
Alternatively, simply consider specifying the corresponding JDBC types explicitly:
either via a 'BatchPreparedStatementSetter' as shown above, or via an explicit type
array given to a 'List<Object[]>' based call, or via 'registerSqlType' calls on a
custom 'MapSqlParameterSource' instance, or via a 'BeanPropertySqlParameterSource'
which derives the SQL type from the Java-declared property type even for a null value.
====
[[jdbc-batch-multi]] [[jdbc-batch-multi]]
==== Batch operations with multiple batches ==== Batch operations with multiple batches
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册