Quote CONDITION whenever necessary for JDBC

SO: https://stackoverflow.com/questions/70286480/how-can-spring-integration-5-5-x-use-mysql-as-message-store

Turns out the condition word is reserved in many SQL DB vendors, e.g.:
https://dev.mysql.com/doc/refman/8.0/en/keywords.html

* Fix SQL scripts for quoting `CONDITION` column name for
those vendors which have it as a reserved word
* Fix `JdbcMessageStore` to have a `CONDITION` quoted to `""` by default and
replaced to "`" for MySQL
* Add `MySqlContainerTest` to test against MySQL Docker container
* Looks like quoted identifiers work well even if they are not reserved words in the SQL vendor
This commit is contained in:
Artem Bilan
2021-12-10 14:21:02 -05:00
committed by Gary Russell
parent 3c32daa25a
commit b424cbe171
12 changed files with 180 additions and 85 deletions

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jdbc.mysql;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.integration.test.util.TestUtils;
/**
* The base contract for JUnit tests based on the container for MqSQL.
*
* @author Artem Bilan
*
* @since 5.5.7
*/
@Testcontainers(disabledWithoutDocker = true)
public interface MySqlContainerTest {
@Container
MySQLContainer<?> MY_SQL_CONTAINER =
new MySQLContainer<>(TestUtils.dockerRegistryFromEnv() + "mysql:latest")
.withReuse(true);
static String getDriverClassName() {
return MY_SQL_CONTAINER.getDriverClassName();
}
static String getJdbcUrl() {
return MY_SQL_CONTAINER.getJdbcUrl();
}
static String getUsername() {
return MY_SQL_CONTAINER.getUsername();
}
static String getPassword() {
return MY_SQL_CONTAINER.getPassword();
}
}

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/int30"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="10"/>
<property name="defaultAutoCommit" value="false"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>

View File

@@ -25,59 +25,48 @@ import java.util.UUID;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.jdbc.store.JdbcMessageStore;
import org.springframework.integration.jdbc.store.JdbcMessageStoreTests;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.predicate.MessagePredicate;
import org.springframework.integration.util.UUIDConverter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.annotation.Repeat;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
/**
* Based on the test for Derby:
*
* {@link JdbcMessageStoreTests}
*
* This tests requires at least MySql 5.6.4 as it uses the fractional second support
* in that version. For more information, please see:
*
* https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html
*
* Also, please make sure you are using the respective DDL scripts:
*
* schema-mysql-5_6_4.sql
*
* @author Gunnar Hillert
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@Ignore
public class MySqlJdbcMessageStoreTests {
@SpringJUnitConfig
@DirtiesContext
public class MySqlJdbcMessageStoreTests implements MySqlContainerTest {
private static final Log LOG = LogFactory.getLog(MySqlJdbcMessageStoreTests.class);
@@ -89,13 +78,13 @@ public class MySqlJdbcMessageStoreTests {
@Autowired
private PlatformTransactionManager transactionManager;
@Before
@BeforeEach
public void init() {
messageStore = new JdbcMessageStore(dataSource);
messageStore.setRegion("JdbcMessageStoreTests");
}
@After
@AfterEach
public void afterTest() {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
new TransactionTemplate(this.transactionManager).execute(status -> {
@@ -203,7 +192,7 @@ public class MySqlJdbcMessageStoreTests {
Message<String> message = MessageBuilder.withPayload("foo").build();
message = messageStore.addMessage(message);
Message<String> result = messageStore.addMessage(message);
assertThat(result).isSameAs(message);
assertThat(result).isEqualTo(message);
}
@Test
@@ -336,6 +325,7 @@ public class MySqlJdbcMessageStoreTests {
@Test
@Transactional
@Disabled("Time sensitive")
public void testExpireMessageGroupOnCreateOnly() throws Exception {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
@@ -355,6 +345,7 @@ public class MySqlJdbcMessageStoreTests {
@Test
@Transactional
@Disabled("Time sensitive")
public void testExpireMessageGroupOnIdleOnly() throws Exception {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
@@ -496,4 +487,48 @@ public class MySqlJdbcMessageStoreTests {
.isEqualTo(2);
}
@Test
public void testMessageGroupCondition() {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").build();
this.messageStore.addMessagesToGroup(groupId, message);
this.messageStore.setGroupCondition(groupId, "testCondition");
assertThat(this.messageStore.getMessageGroup(groupId).getCondition()).isEqualTo("testCondition");
}
@Configuration
public static class Config {
@Value("org/springframework/integration/jdbc/schema-mysql.sql")
Resource createSchemaScript;
@Value("org/springframework/integration/jdbc/schema-drop-mysql.sql")
Resource dropSchemaScript;
@Bean
DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(MySqlContainerTest.getDriverClassName());
dataSource.setUrl(MySqlContainerTest.getJdbcUrl());
dataSource.setUsername(MySqlContainerTest.getUsername());
dataSource.setPassword(MySqlContainerTest.getPassword());
return dataSource;
}
@Bean
PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
DataSourceInitializer dataSourceInitializer() {
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(dataSource());
dataSourceInitializer.setDatabasePopulator(new ResourceDatabasePopulator(this.createSchemaScript));
dataSourceInitializer.setDatabaseCleaner(new ResourceDatabasePopulator(this.dropSchemaScript));
return dataSourceInitializer;
}
}
}

View File

@@ -5,9 +5,9 @@
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="org/springframework/integration/jdbc/schema-drop-h2.sql"/>
<jdbc:script location="org/springframework/integration/jdbc/schema-h2.sql" />
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="org/springframework/integration/jdbc/schema-drop-hsqldb.sql"/>
<jdbc:script location="org/springframework/integration/jdbc/schema-hsqldb.sql" />
</jdbc:embedded-database>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

View File

@@ -30,9 +30,8 @@ import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
@@ -47,8 +46,7 @@ import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -60,8 +58,7 @@ import org.springframework.transaction.annotation.Transactional;
* @author Gary Russell
* @author Will Schipp
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
@DirtiesContext // close at the end after class
@Transactional
public class JdbcMessageStoreTests {
@@ -71,7 +68,7 @@ public class JdbcMessageStoreTests {
private JdbcMessageStore messageStore;
@Before
@BeforeEach
public void init() {
messageStore = new JdbcMessageStore(dataSource);
}
@@ -219,12 +216,12 @@ public class JdbcMessageStoreTests {
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
String groupId = "X";
this.messageStore.setRemoveBatchSize(10);
List<Message<?>> messages = new ArrayList<Message<?>>();
List<Message<?>> messages = new ArrayList<>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messages.add(message);
}
this.messageStore.addMessagesToGroup(groupId, messages.toArray(new Message<?>[messages.size()]));
this.messageStore.addMessagesToGroup(groupId, messages.toArray(new Message<?>[0]));
MessageGroup group = this.messageStore.getMessageGroup(groupId);
assertThat(group.size()).isEqualTo(25);
this.messageStore.removeMessagesFromGroup(groupId, messages);
@@ -261,7 +258,7 @@ public class JdbcMessageStoreTests {
}
@Test
public void testUpdateLastReleasedSequence() throws Exception {
public void testUpdateLastReleasedSequence() {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessagesToGroup(groupId, message);
@@ -271,7 +268,7 @@ public class JdbcMessageStoreTests {
}
@Test
public void testMessageGroupCount() throws Exception {
public void testMessageGroupCount() {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").build();
messageStore.addMessagesToGroup(groupId, message);
@@ -279,7 +276,7 @@ public class JdbcMessageStoreTests {
}
@Test
public void testMessageGroupSizes() throws Exception {
public void testMessageGroupSizes() {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").build();
messageStore.addMessagesToGroup(groupId, message);
@@ -408,7 +405,7 @@ public class JdbcMessageStoreTests {
}
@Test
public void testSameMessageToMultipleGroups() throws Exception {
public void testSameMessageToMultipleGroups() {
final String group1Id = "group1";
final String group2Id = "group2";
@@ -522,4 +519,13 @@ public class JdbcMessageStoreTests {
assertThat(messageGroup.isComplete()).isTrue();
}
@Test
public void testMessageGroupCondition() {
String groupId = "X";
Message<String> message = MessageBuilder.withPayload("foo").build();
this.messageStore.addMessagesToGroup(groupId, message);
this.messageStore.setGroupCondition(groupId, "testCondition");
assertThat(this.messageStore.getMessageGroup(groupId).getCondition()).isEqualTo("testCondition");
}
}