Skip to content

Commit daee6ee

Browse files
committed
ParameterMetaData.getParameterType performance on Oracle 12c
Issue: SPR-16139
1 parent a8b4884 commit daee6ee

File tree

1 file changed

+37
-24
lines changed

1 file changed

+37
-24
lines changed

src/docs/asciidoc/data-access.adoc

+37-24
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ strategy__. A transaction strategy is defined by the
160160
----
161161
public interface PlatformTransactionManager {
162162
163-
TransactionStatus getTransaction(
164-
TransactionDefinition definition) throws TransactionException;
163+
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
165164
166165
void commit(TransactionStatus status) throws TransactionException;
167166
@@ -2990,10 +2989,10 @@ An `update()` convenience method supports the retrieval of primary keys generate
29902989
database. This support is part of the JDBC 3.0 standard; see Chapter 13.6 of the
29912990
specification for details. The method takes a `PreparedStatementCreator` as its first
29922991
argument, and this is the way the required insert statement is specified. The other
2993-
argument is a `KeyHolder`, which contains the generated key on successful return from
2994-
the update. There is not a standard single way to create an appropriate
2995-
`PreparedStatement` (which explains why the method signature is the way it is). The
2996-
following example works on Oracle but may not work on other platforms:
2992+
argument is a `KeyHolder`, which contains the generated key on successful return from the
2993+
update. There is not a standard single way to create an appropriate `PreparedStatement`
2994+
(which explains why the method signature is the way it is). The following example works
2995+
on Oracle but may not work on other platforms:
29972996

29982997
[source,java,indent=0]
29992998
[subs="verbatim,quotes"]
@@ -3244,27 +3243,26 @@ based on entries in a list. The entire list is used as the batch in this example
32443243
[subs="verbatim,quotes"]
32453244
----
32463245
public class JdbcActorDao implements ActorDao {
3246+
32473247
private JdbcTemplate jdbcTemplate;
32483248
32493249
public void setDataSource(DataSource dataSource) {
32503250
this.jdbcTemplate = new JdbcTemplate(dataSource);
32513251
}
32523252
32533253
public int[] batchUpdate(final List<Actor> actors) {
3254-
int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, " +
3255-
"last_name = ? where id = ?",
3256-
new BatchPreparedStatementSetter() {
3257-
public void setValues(PreparedStatement ps, int i) throws SQLException {
3254+
return this.jdbcTemplate.batchUpdate(
3255+
"update t_actor set first_name = ?, last_name = ? where id = ?",
3256+
new BatchPreparedStatementSetter() {
3257+
public void setValues(PreparedStatement ps, int i) throws SQLException {
32583258
ps.setString(1, actors.get(i).getFirstName());
32593259
ps.setString(2, actors.get(i).getLastName());
32603260
ps.setLong(3, actors.get(i).getId().longValue());
32613261
}
3262-
32633262
public int getBatchSize() {
32643263
return actors.size();
32653264
}
32663265
});
3267-
return updateCounts;
32683266
}
32693267
32703268
// ... additional methods
@@ -3287,27 +3285,27 @@ provide all parameter values in the call as a list. The framework loops over the
32873285
values and uses an internal prepared statement setter. The API varies depending on
32883286
whether you use named parameters. For the named parameters you provide an array of
32893287
`SqlParameterSource`, one entry for each member of the batch. You can use the
3290-
`SqlParameterSource.createBatch` method to create this array, passing in either an array
3291-
of JavaBeans or an array of Maps containing the parameter values.
3288+
`SqlParameterSourceUtils.createBatch` convenience methods to create this array, passing
3289+
in an array of bean-style objects (with getter methods corresponding to parameters)
3290+
and/or String-keyed Maps (containing the corresponding parameters as values).
32923291

32933292
This example shows a batch update using named parameters:
32943293

32953294
[source,java,indent=0]
32963295
[subs="verbatim,quotes"]
32973296
----
32983297
public class JdbcActorDao implements ActorDao {
3298+
32993299
private NamedParameterTemplate namedParameterJdbcTemplate;
33003300
33013301
public void setDataSource(DataSource dataSource) {
33023302
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
33033303
}
33043304
3305-
public int[] batchUpdate(final List<Actor> actors) {
3306-
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray());
3307-
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
3305+
public int[] batchUpdate(List<Actor> actors) {
3306+
return this.namedParameterJdbcTemplate.batchUpdate(
33083307
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
3309-
batch);
3310-
return updateCounts;
3308+
SqlParameterSourceUtils.createBatch(actors));
33113309
}
33123310
33133311
// ... additional methods
@@ -3336,15 +3334,12 @@ The same example using classic JDBC "?" placeholders:
33363334
List<Object[]> batch = new ArrayList<Object[]>();
33373335
for (Actor actor : actors) {
33383336
Object[] values = new Object[] {
3339-
actor.getFirstName(),
3340-
actor.getLastName(),
3341-
actor.getId()};
3337+
actor.getFirstName(), actor.getLastName(), actor.getId()};
33423338
batch.add(values);
33433339
}
3344-
int[] updateCounts = jdbcTemplate.batchUpdate(
3340+
return this.jdbcTemplate.batchUpdate(
33453341
"update t_actor set first_name = ?, last_name = ? where id = ?",
33463342
batch);
3347-
return updateCounts;
33483343
}
33493344
33503345
// ... additional methods
@@ -3355,6 +3350,24 @@ All of the above batch update methods return an int array containing the number
33553350
affected rows for each batch entry. This count is reported by the JDBC driver. If the
33563351
count is not available, the JDBC driver returns a -2 value.
33573352

3353+
[NOTE]
3354+
====
3355+
In such a scenario with automatic setting of values on an underlying `PreparedStatement`,
3356+
the corresponding JDBC type for each value needs to be derived from the given Java type.
3357+
While this usually works well, there is a potential for issues, e.g. with Map-contained
3358+
`null` values: Spring will by default call `ParameterMetaData.getParameterType` in such a
3359+
case which may be expensive with your JDBC driver. Please make sure to use a recent driver
3360+
version, and consider setting the "spring.jdbc.getParameterType.ignore" property to "true"
3361+
(as a JVM system property or in a `spring.properties` file in the root of your classpath)
3362+
if you encounter a performance issue, e.g. as reported on Oracle 12c (SPR-16139).
3363+
3364+
Alternatively, simply consider specifying the corresponding JDBC types explicitly:
3365+
either via a 'BatchPreparedStatementSetter' as shown above, or via an explicit type
3366+
array given to a 'List<Object[]>' based call, or via 'registerSqlType' calls on a
3367+
custom 'MapSqlParameterSource' instance, or via a 'BeanPropertySqlParameterSource'
3368+
which derives the SQL type from the Java-declared property type even for a null value.
3369+
====
3370+
33583371

33593372
[[jdbc-batch-multi]]
33603373
==== Batch operations with multiple batches

0 commit comments

Comments
 (0)