Skip to content

Commit 697108c

Browse files
committed
Support safe-updates mode in MySQLMaxValueIncrementer
Prior to this commit, MySQLMaxValueIncrementer could not be used when the MySQL database was configured to use safe-updates mode. See https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html#safe-updates This commit introduces a `limit 1` clause to the generated update statement to allow MySQLMaxValueIncrementer to be compatible with MySQL safe-updates mode. Closes gh-26858
1 parent 607d918 commit 697108c

File tree

2 files changed

+23
-14
lines changed

2 files changed

+23
-14
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/support/incrementer/MySQLMaxValueIncrementer.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -40,22 +40,27 @@
4040
*
4141
* <p>Example:
4242
*
43-
* <pre class="code">create table tab (id int unsigned not null primary key, text varchar(100));
43+
* <pre class="code">
44+
* create table tab (id int unsigned not null primary key, text varchar(100));
4445
* create table tab_sequence (value int not null);
4546
* insert into tab_sequence values(0);</pre>
4647
*
47-
* If "cacheSize" is set, the intermediate values are served without querying the
48+
* <p>If {@code cacheSize} is set, the intermediate values are served without querying the
4849
* database. If the server or your application is stopped or crashes or a transaction
4950
* is rolled back, the unused values will never be served. The maximum hole size in
50-
* numbering is consequently the value of cacheSize.
51+
* numbering is consequently the value of {@code cacheSize}.
5152
*
5253
* <p>It is possible to avoid acquiring a new connection for the incrementer by setting the
5354
* "useNewConnection" property to false. In this case you <i>MUST</i> use a non-transactional
5455
* storage engine like MYISAM when defining the incrementer table.
5556
*
57+
* <p>As of Spring Framework 5.3.7, {@code MySQLMaxValueIncrementer} is compatible with
58+
* <a href="https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html#safe-updates">MySQL safe updates mode</a>.
59+
*
5660
* @author Jean-Pierre Pawlak
5761
* @author Thomas Risberg
5862
* @author Juergen Hoeller
63+
* @author Sam Brannen
5964
*/
6065
public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer {
6166

@@ -141,7 +146,7 @@ protected synchronized long getNextKey() throws DataAccessException {
141146
String columnName = getColumnName();
142147
try {
143148
stmt.executeUpdate("update " + getIncrementerName() + " set " + columnName +
144-
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
149+
" = last_insert_id(" + columnName + " + " + getCacheSize() + ") limit 1");
145150
}
146151
catch (SQLException ex) {
147152
throw new DataAccessResourceFailureException("Could not increment " + columnName + " for " +

spring-jdbc/src/test/java/org/springframework/jdbc/support/DataFieldMaxValueIncrementerTests.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -25,6 +25,7 @@
2525

2626
import org.junit.jupiter.api.Test;
2727

28+
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
2829
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
2930
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
3031
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
@@ -38,10 +39,13 @@
3839
import static org.mockito.Mockito.verify;
3940

4041
/**
42+
* Unit tests for {@link DataFieldMaxValueIncrementer} implementations.
43+
*
4144
* @author Juergen Hoeller
45+
* @author Sam Brannen
4246
* @since 27.02.2004
4347
*/
44-
public class DataFieldMaxValueIncrementerTests {
48+
class DataFieldMaxValueIncrementerTests {
4549

4650
private final DataSource dataSource = mock(DataSource.class);
4751

@@ -53,7 +57,7 @@ public class DataFieldMaxValueIncrementerTests {
5357

5458

5559
@Test
56-
public void testHanaSequenceMaxValueIncrementer() throws SQLException {
60+
void hanaSequenceMaxValueIncrementer() throws SQLException {
5761
given(dataSource.getConnection()).willReturn(connection);
5862
given(connection.createStatement()).willReturn(statement);
5963
given(statement.executeQuery("select myseq.nextval from dummy")).willReturn(resultSet);
@@ -75,7 +79,7 @@ public void testHanaSequenceMaxValueIncrementer() throws SQLException {
7579
}
7680

7781
@Test
78-
public void testHsqlMaxValueIncrementer() throws SQLException {
82+
void hsqlMaxValueIncrementer() throws SQLException {
7983
given(dataSource.getConnection()).willReturn(connection);
8084
given(connection.createStatement()).willReturn(statement);
8185
given(statement.executeQuery("select max(identity()) from myseq")).willReturn(resultSet);
@@ -105,7 +109,7 @@ public void testHsqlMaxValueIncrementer() throws SQLException {
105109
}
106110

107111
@Test
108-
public void testHsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLException {
112+
void hsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLException {
109113
given(dataSource.getConnection()).willReturn(connection);
110114
given(connection.createStatement()).willReturn(statement);
111115
given(statement.executeQuery("select max(identity()) from myseq")).willReturn(resultSet);
@@ -136,7 +140,7 @@ public void testHsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLExce
136140
}
137141

138142
@Test
139-
public void testMySQLMaxValueIncrementer() throws SQLException {
143+
void mySQLMaxValueIncrementer() throws SQLException {
140144
given(dataSource.getConnection()).willReturn(connection);
141145
given(connection.createStatement()).willReturn(statement);
142146
given(statement.executeQuery("select last_insert_id()")).willReturn(resultSet);
@@ -156,14 +160,14 @@ public void testMySQLMaxValueIncrementer() throws SQLException {
156160
assertThat(incrementer.nextStringValue()).isEqualTo("3");
157161
assertThat(incrementer.nextLongValue()).isEqualTo(4);
158162

159-
verify(statement, times(2)).executeUpdate("update myseq set seq = last_insert_id(seq + 2)");
163+
verify(statement, times(2)).executeUpdate("update myseq set seq = last_insert_id(seq + 2) limit 1");
160164
verify(resultSet, times(2)).close();
161165
verify(statement, times(2)).close();
162166
verify(connection, times(2)).close();
163167
}
164168

165169
@Test
166-
public void testOracleSequenceMaxValueIncrementer() throws SQLException {
170+
void oracleSequenceMaxValueIncrementer() throws SQLException {
167171
given(dataSource.getConnection()).willReturn(connection);
168172
given(connection.createStatement()).willReturn(statement);
169173
given(statement.executeQuery("select myseq.nextval from dual")).willReturn(resultSet);
@@ -185,7 +189,7 @@ public void testOracleSequenceMaxValueIncrementer() throws SQLException {
185189
}
186190

187191
@Test
188-
public void testPostgresSequenceMaxValueIncrementer() throws SQLException {
192+
void postgresSequenceMaxValueIncrementer() throws SQLException {
189193
given(dataSource.getConnection()).willReturn(connection);
190194
given(connection.createStatement()).willReturn(statement);
191195
given(statement.executeQuery("select nextval('myseq')")).willReturn(resultSet);

0 commit comments

Comments
 (0)