Skip to content

Commit d76174e

Browse files
committed
GH-9192: Deprecate LobHandler usage
Fixes: #9192 With modern drivers we don't need BLOB-specific handling anymore. The regular `PreparedStatement.setBytes()` and `ResultSet.getBytes()` are enough for our serialized messages
1 parent 753916c commit d76174e

File tree

10 files changed

+610
-77
lines changed

10 files changed

+610
-77
lines changed

spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/store/JdbcChannelMessageStore.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ private enum Query {
143143

144144
private SerializingConverter serializer;
145145

146-
private LobHandler lobHandler = new DefaultLobHandler();
147-
148146
private MessageRowMapper messageRowMapper;
149147

150148
private ChannelMessageStorePreparedStatementSetter preparedStatementSetter;
@@ -231,10 +229,10 @@ public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
231229
* Override the {@link LobHandler} that is used to create and unpack large objects in SQL queries. The default is
232230
* fine for almost all platforms, but some Oracle drivers require a native implementation.
233231
* @param lobHandler a {@link LobHandler}
232+
* @deprecated since 6.4 (for removal) (with no replacement) in favor of plain JDBC driver support for byte arrays.
234233
*/
234+
@Deprecated(forRemoval = true, since = "6.4")
235235
public void setLobHandler(LobHandler lobHandler) {
236-
Assert.notNull(lobHandler, "The provided LobHandler must not be null.");
237-
this.lobHandler = lobHandler;
238236
}
239237

240238
/**
@@ -396,8 +394,8 @@ protected MessageGroupFactory getMessageGroupFactory() {
396394
* and {@link ChannelMessageStorePreparedStatementSetter} was explicitly set using
397395
* {@link #setMessageRowMapper(MessageRowMapper)} and
398396
* {@link #setPreparedStatementSetter(ChannelMessageStorePreparedStatementSetter)} respectively, the default
399-
* {@link MessageRowMapper} and {@link ChannelMessageStorePreparedStatementSetter} will be instantiate using the
400-
* specified {@link #deserializer} and {@link #lobHandler}.
397+
* {@link MessageRowMapper} and {@link ChannelMessageStorePreparedStatementSetter} will be instantiated using the
398+
* specified {@link #deserializer}.
401399
* Also, if the jdbcTemplate's fetchSize property ({@link JdbcTemplate#getFetchSize()})
402400
* is not 1, a warning will be logged. When using the {@link JdbcChannelMessageStore}
403401
* with Oracle, the fetchSize value of 1 is needed to ensure FIFO characteristics
@@ -409,16 +407,15 @@ public void afterPropertiesSet() {
409407
Assert.notNull(this.channelMessageStoreQueryProvider, "A channelMessageStoreQueryProvider must be provided.");
410408

411409
if (this.messageRowMapper == null) {
412-
this.messageRowMapper = new MessageRowMapper(this.deserializer, this.lobHandler);
410+
this.messageRowMapper = new MessageRowMapper(this.deserializer);
413411
}
414412

415413
if (this.jdbcTemplate.getFetchSize() != 1) {
416414
LOGGER.warn("The jdbcTemplate's fetch size is not 1. This may cause FIFO issues with Oracle databases.");
417415
}
418416

419417
if (this.preparedStatementSetter == null) {
420-
this.preparedStatementSetter = new ChannelMessageStorePreparedStatementSetter(this.serializer,
421-
this.lobHandler);
418+
this.preparedStatementSetter = new ChannelMessageStorePreparedStatementSetter(this.serializer);
422419
}
423420
this.jdbcTemplate.afterPropertiesSet();
424421
}

spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/store/JdbcMessageStore.java

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.integration.jdbc.store;
1818

19-
import java.sql.ResultSet;
20-
import java.sql.SQLException;
2119
import java.sql.Timestamp;
2220
import java.util.Arrays;
2321
import java.util.Collection;
@@ -38,6 +36,7 @@
3836
import org.springframework.core.serializer.support.SerializingConverter;
3937
import org.springframework.dao.DataIntegrityViolationException;
4038
import org.springframework.dao.IncorrectResultSizeDataAccessException;
39+
import org.springframework.integration.jdbc.store.channel.MessageRowMapper;
4140
import org.springframework.integration.store.AbstractMessageGroupStore;
4241
import org.springframework.integration.store.MessageGroup;
4342
import org.springframework.integration.store.MessageGroupMetadata;
@@ -49,9 +48,7 @@
4948
import org.springframework.integration.util.UUIDConverter;
5049
import org.springframework.jdbc.core.JdbcOperations;
5150
import org.springframework.jdbc.core.JdbcTemplate;
52-
import org.springframework.jdbc.core.RowMapper;
5351
import org.springframework.jdbc.core.SingleColumnRowMapper;
54-
import org.springframework.jdbc.support.lob.DefaultLobHandler;
5552
import org.springframework.jdbc.support.lob.LobHandler;
5653
import org.springframework.jmx.export.annotation.ManagedAttribute;
5754
import org.springframework.lang.Nullable;
@@ -253,8 +250,6 @@ public String getSql() {
253250
}
254251
}
255252

256-
private final MessageMapper mapper = new MessageMapper();
257-
258253
private final JdbcOperations jdbcTemplate;
259254

260255
private final Map<Query, String> queryCache = new ConcurrentHashMap<>();
@@ -270,9 +265,9 @@ public String getSql() {
270265

271266
private boolean deserializerExplicitlySet;
272267

273-
private SerializingConverter serializer;
268+
private MessageRowMapper mapper = new MessageRowMapper(this.deserializer);
274269

275-
private LobHandler lobHandler = new DefaultLobHandler();
270+
private SerializingConverter serializer;
276271

277272
private boolean checkDatabaseOnStart = true;
278273

@@ -325,9 +320,11 @@ public void setRegion(String region) {
325320
* Override the {@link LobHandler} that is used to create and unpack large objects in SQL queries. The default is
326321
* fine for almost all platforms, but some Oracle drivers require a native implementation.
327322
* @param lobHandler a {@link LobHandler}
323+
* @deprecated since 6.4 (for removal) (with no replacement) in favor of plain JDBC driver support for byte arrays.
328324
*/
325+
@Deprecated(forRemoval = true, since = "6.4")
329326
public void setLobHandler(LobHandler lobHandler) {
330-
this.lobHandler = lobHandler;
327+
331328
}
332329

333330
/**
@@ -347,6 +344,7 @@ public void setSerializer(Serializer<? super Message<?>> serializer) {
347344
public void setDeserializer(Deserializer<? extends Message<?>> deserializer) {
348345
this.deserializer = new AllowListDeserializingConverter((Deserializer) deserializer);
349346
this.deserializerExplicitlySet = true;
347+
this.mapper = new MessageRowMapper(this.deserializer);
350348
}
351349

352350
/**
@@ -459,8 +457,7 @@ public <T> Message<T> addMessage(final Message<T> message) {
459457
ps.setString(1, messageId); // NOSONAR - magic number
460458
ps.setString(2, this.region); // NOSONAR - magic number
461459
ps.setTimestamp(3, new Timestamp(System.currentTimeMillis())); // NOSONAR - magic number
462-
463-
this.lobHandler.getLobCreator().setBlobAsBytes(ps, 4, messageBytes); // NOSONAR - magic number
460+
ps.setBytes(4, messageBytes); // NOSONAR - magic number
464461
});
465462
}
466463
catch (DataIntegrityViolationException ex) {
@@ -780,23 +777,4 @@ private String getKey(Object input) {
780777
return input == null ? null : UUIDConverter.getUUID(input).toString();
781778
}
782779

783-
/**
784-
* Convenience class to be used to unpack a message from a result set row. Uses column named in the result set to
785-
* extract the required data, so that select clause ordering is unimportant.
786-
*/
787-
private final class MessageMapper implements RowMapper<Message<?>> {
788-
789-
@Override
790-
public Message<?> mapRow(ResultSet rs, int rowNum) throws SQLException {
791-
byte[] messageBytes = JdbcMessageStore.this.lobHandler.getBlobAsBytes(rs, "MESSAGE_BYTES");
792-
if (messageBytes == null) {
793-
return null;
794-
}
795-
else {
796-
return (Message<?>) JdbcMessageStore.this.deserializer.convert(messageBytes);
797-
}
798-
}
799-
800-
}
801-
802780
}

spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/store/channel/ChannelMessageStorePreparedStatementSetter.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2017-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@
3939
* <p>
4040
* This class can be extended for any custom data structure or columns types.
4141
* For this purpose the {@code protected} constructor is provided for inheritors.
42-
* In this case the {@link #serializer} and {@link #lobHandler} are null to avoid
42+
* In this case the {@link #serializer} is {@code null} to avoid
4343
* extra serialization actions if the target custom behavior doesn't imply them.
4444
*
4545
* @author Meherzad Lahewala
@@ -53,33 +53,42 @@ public class ChannelMessageStorePreparedStatementSetter {
5353

5454
private final SerializingConverter serializer;
5555

56-
private final LobHandler lobHandler;
57-
5856
/**
5957
* Instantiate a {@link ChannelMessageStorePreparedStatementSetter} with the provided
6058
* serializer and lobHandler, which both must not be null.
6159
* @param serializer the {@link SerializingConverter} to build {@code byte[]} from
6260
* the request message
6361
* @param lobHandler the {@link LobHandler} to store {@code byte[]} of the request
6462
* message to prepared statement
63+
* @deprecated since 6.4 (for removal) (if favor of {@link #ChannelMessageStorePreparedStatementSetter(SerializingConverter)})
64+
* with a plain JDBC driver support for byte arrays.
6565
*/
66+
@Deprecated(forRemoval = true, since = "6.4")
6667
public ChannelMessageStorePreparedStatementSetter(SerializingConverter serializer, LobHandler lobHandler) {
68+
this(serializer);
69+
}
70+
71+
/**
72+
* Instantiate a {@link ChannelMessageStorePreparedStatementSetter} with the provided
73+
* serializer and lobHandler, which both must not be null.
74+
* @param serializer the {@link SerializingConverter} to build {@code byte[]} from
75+
* the request message
76+
* @since 6.4
77+
*/
78+
public ChannelMessageStorePreparedStatementSetter(SerializingConverter serializer) {
6779
Assert.notNull(serializer, "'serializer' must not be null");
68-
Assert.notNull(lobHandler, "'lobHandler' must not be null");
6980
this.serializer = serializer;
70-
this.lobHandler = lobHandler;
7181
}
7282

7383
/**
7484
* The default constructor for inheritors who are not interested in the message
7585
* serialization to {@code byte[]}.
76-
* The {@link #serializer} and {@link #lobHandler} are null from this constructor,
86+
* The {@link #serializer} is {@code null} from this constructor,
7787
* therefore any serialization isn't happened in the default {@link #setValues} implementation.
7888
* A target implementor must ensure the proper custom logic for storing message.
7989
*/
8090
protected ChannelMessageStorePreparedStatementSetter() {
8191
this.serializer = null;
82-
this.lobHandler = null;
8392
}
8493

8594
/**
@@ -91,15 +100,15 @@ protected ChannelMessageStorePreparedStatementSetter() {
91100
* <li>3 - region
92101
* <li>4 - createdDate
93102
* <li>5 - priority if enabled, otherwise null
94-
* <li>6 - serialized message if {@link #serializer} and {@link #lobHandler} are provided.
103+
* <li>6 - serialized message if {@link #serializer} is provided.
95104
* </ul>
96105
* An inheritor may consider to call this method for population common properties and perform
97106
* custom message serialization logic for the parameter #6.
98107
* Any custom data structure population can be achieved with full overriding of this method.
99108
* @param preparedStatement the {@link PreparedStatement} to populate columns based on the provided arguments
100109
* @param requestMessage the {@link Message} to store
101110
* @param groupId the group id for the message to store
102-
* @param region the region in the target table to distinguish different data base clients
111+
* @param region the region in the target table to distinguish different database clients
103112
* @param priorityEnabled the flag to indicate if priority has to be stored
104113
* @throws SQLException the exception throws during data population
105114
*/
@@ -126,7 +135,7 @@ public void setValues(PreparedStatement preparedStatement, Message<?> requestMes
126135

127136
if (this.serializer != null) {
128137
byte[] messageBytes = this.serializer.convert(requestMessage);
129-
this.lobHandler.getLobCreator().setBlobAsBytes(preparedStatement, 6, messageBytes); // NOSONAR magic number
138+
preparedStatement.setBytes(6, messageBytes); // NOSONAR magic number
130139
}
131140
}
132141

spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/store/channel/MessageRowMapper.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
import org.springframework.jdbc.core.RowMapper;
2424
import org.springframework.jdbc.support.lob.LobHandler;
2525
import org.springframework.messaging.Message;
26+
import org.springframework.util.Assert;
2627

2728
/**
2829
* Convenience class to be used to unpack a {@link Message} from a result set
@@ -40,22 +41,32 @@ public class MessageRowMapper implements RowMapper<Message<?>> {
4041

4142
private final AllowListDeserializingConverter deserializer;
4243

43-
private final LobHandler lobHandler;
44-
4544
/**
4645
* Construct an instance based on the provided {@link AllowListDeserializingConverter}
4746
* and {@link LobHandler}.
4847
* @param deserializer the {@link AllowListDeserializingConverter} to use.
4948
* @param lobHandler the {@link LobHandler} to use.
49+
* @deprecated since 6.4 (for removal) (if favor of {@link #MessageRowMapper(AllowListDeserializingConverter)})
50+
* with a plain JDBC driver support for byte arrays.
5051
*/
52+
@Deprecated(forRemoval = true, since = "6.4")
5153
public MessageRowMapper(AllowListDeserializingConverter deserializer, LobHandler lobHandler) {
54+
this(deserializer);
55+
}
56+
57+
/**
58+
* Construct an instance based on the provided {@link AllowListDeserializingConverter}.
59+
* @param deserializer the {@link AllowListDeserializingConverter} to use.
60+
* @since 6.4
61+
*/
62+
public MessageRowMapper(AllowListDeserializingConverter deserializer) {
63+
Assert.notNull(deserializer, "'deserializer' must not be null");
5264
this.deserializer = deserializer;
53-
this.lobHandler = lobHandler;
5465
}
5566

5667
@Override
5768
public Message<?> mapRow(ResultSet rs, int rowNum) throws SQLException {
58-
byte[] blobAsBytes = this.lobHandler.getBlobAsBytes(rs, "MESSAGE_BYTES");
69+
byte[] blobAsBytes = rs.getBytes("MESSAGE_BYTES");
5970
if (blobAsBytes == null) {
6071
return null;
6172
}

spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParserTests.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.integration.store.MessageStore;
3333
import org.springframework.integration.support.MessageBuilder;
3434
import org.springframework.integration.test.util.TestUtils;
35-
import org.springframework.jdbc.support.lob.LobHandler;
3635
import org.springframework.messaging.Message;
3736
import org.springframework.test.util.ReflectionTestUtils;
3837

@@ -52,24 +51,24 @@ public class JdbcMessageStoreParserTests {
5251
public void testSimpleMessageStoreWithDataSource() {
5352
setUp("defaultJdbcMessageStore.xml", getClass());
5453
MessageStore store = context.getBean("messageStore", MessageStore.class);
55-
assertThat(store instanceof JdbcMessageStore).isTrue();
54+
assertThat(store).isInstanceOf(JdbcMessageStore.class);
5655
}
5756

5857
@Test
5958
public void testSimpleMessageStoreWithTemplate() {
6059
setUp("jdbcOperationsJdbcMessageStore.xml", getClass());
6160
MessageStore store = context.getBean("messageStore", MessageStore.class);
62-
assertThat(store instanceof JdbcMessageStore).isTrue();
61+
assertThat(store).isInstanceOf(JdbcMessageStore.class);
6362
}
6463

6564
@Test
6665
public void testSimpleMessageStoreWithSerializer() {
6766
setUp("serializerJdbcMessageStore.xml", getClass());
6867
MessageStore store = context.getBean("messageStore", MessageStore.class);
6968
Object serializer = TestUtils.getPropertyValue(store, "serializer.serializer");
70-
assertThat(serializer instanceof EnhancedSerializer).isTrue();
69+
assertThat(serializer).isInstanceOf(EnhancedSerializer.class);
7170
Object deserializer = TestUtils.getPropertyValue(store, "deserializer.deserializer");
72-
assertThat(deserializer instanceof EnhancedSerializer).isTrue();
71+
assertThat(deserializer).isInstanceOf(EnhancedSerializer.class);
7372
}
7473

7574
@Test
@@ -78,7 +77,6 @@ public void testMessageStoreWithAttributes() {
7877
MessageStore store = context.getBean("messageStore", MessageStore.class);
7978
assertThat(ReflectionTestUtils.getField(store, "region")).isEqualTo("FOO");
8079
assertThat(ReflectionTestUtils.getField(store, "tablePrefix")).isEqualTo("BAR_");
81-
assertThat(ReflectionTestUtils.getField(store, "lobHandler")).isEqualTo(context.getBean(LobHandler.class));
8280
}
8381

8482
@AfterEach
@@ -99,8 +97,7 @@ public static class EnhancedSerializer implements Serializer<Object>, Deserializ
9997
private final Deserializer<Object> targetDeserializer = new DefaultDeserializer();
10098

10199
public Object deserialize(InputStream inputStream) throws IOException {
102-
Message<?> message = (Message<?>) targetDeserializer.deserialize(inputStream);
103-
return message;
100+
return targetDeserializer.deserialize(inputStream);
104101
}
105102

106103
public void serialize(Object object, OutputStream outputStream) throws IOException {

spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/soupedUpJdbcMessageStore.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99

1010
<bean id="messageStore" class="org.springframework.integration.jdbc.store.JdbcMessageStore">
1111
<constructor-arg ref="dataSource"/>
12-
<property name="lobHandler" ref="lobHandler"/>
1312
<property name="region" value="FOO"/>
1413
<property name="tablePrefix" value="BAR_"/>
1514
<property name="checkDatabaseOnStart" value="false"/>
1615
</bean>
1716

18-
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"/>
19-
2017
</beans>

0 commit comments

Comments
 (0)