Skip to content

Commit adc328e

Browse files
committed
Added Sequence generation support
1 parent 52fdadd commit adc328e

31 files changed

+381
-22
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java

+18-4
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,27 @@
1515
*/
1616
package org.springframework.data.jdbc.core.convert;
1717

18+
import java.sql.ResultSet;
19+
import java.sql.SQLException;
1820
import java.sql.SQLType;
1921
import java.util.ArrayList;
2022
import java.util.List;
2123
import java.util.Map;
24+
import java.util.Optional;
2225
import java.util.function.Predicate;
2326

2427
import org.springframework.data.jdbc.core.mapping.JdbcValue;
2528
import org.springframework.data.jdbc.support.JdbcUtil;
2629
import org.springframework.data.mapping.PersistentProperty;
2730
import org.springframework.data.mapping.PersistentPropertyAccessor;
2831
import org.springframework.data.relational.core.conversion.IdValueSource;
32+
import org.springframework.data.relational.core.dialect.Dialect;
2933
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
3034
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
3135
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
3236
import org.springframework.data.relational.core.sql.SqlIdentifier;
37+
import org.springframework.jdbc.core.RowMapper;
38+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
3339
import org.springframework.jdbc.support.JdbcUtils;
3440
import org.springframework.lang.Nullable;
3541
import org.springframework.util.Assert;
@@ -46,13 +52,15 @@
4652
public class SqlParametersFactory {
4753
private final RelationalMappingContext context;
4854
private final JdbcConverter converter;
55+
private final Dialect dialect;
4956

50-
/**
51-
* @since 3.1
52-
*/
53-
public SqlParametersFactory(RelationalMappingContext context, JdbcConverter converter) {
57+
private final NamedParameterJdbcOperations operations;
58+
59+
public SqlParametersFactory(RelationalMappingContext context, JdbcConverter converter, Dialect dialect, NamedParameterJdbcOperations operations) {
5460
this.context = context;
5561
this.converter = converter;
62+
this.dialect = dialect;
63+
this.operations = operations;
5664
}
5765

5866
/**
@@ -82,6 +90,12 @@ <T> SqlIdentifierParameterSource forInsert(T instance, Class<T> domainType, Iden
8290
Object idValue = persistentEntity.getIdentifierAccessor(instance).getRequiredIdentifier();
8391
addConvertedPropertyValue(parameterSource, idProperty, idValue, idProperty.getColumnName());
8492
}
93+
if (IdValueSource.SEQUENCE.equals(idValueSource) && persistentEntity.getIdTargetSequence().isPresent()) {
94+
RelationalPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();
95+
String nextSequenceValueSelect = dialect.nextValueFromSequenceSelect(persistentEntity.getIdTargetSequence().get());
96+
Long nextValue = operations.queryForObject(nextSequenceValueSelect, Map.of(), (rs, rowNum) -> rs.getLong(1));
97+
addConvertedPropertyValue(parameterSource, idProperty, nextValue, idProperty.getColumnName());
98+
}
8599
return parameterSource;
86100
}
87101

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC
8686
NamespaceStrategy namespaceStrategy, Dialect dialect) {
8787

8888
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, converter, dialect);
89-
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter);
89+
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect, operations);
9090
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations, dialect);
9191

9292
DataAccessStrategy defaultDataAccessStrategy = new DataAccessStrategyFactory( //

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations op
207207

208208
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, jdbcConverter, dialect);
209209
DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, jdbcConverter, operations,
210-
new SqlParametersFactory(context, jdbcConverter),
210+
new SqlParametersFactory(context, jdbcConverter, dialect, operations),
211211
new InsertStrategyFactory(operations, dialect));
212212

213213
return factory.create();

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public void afterPropertiesSet() {
177177

178178
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext, this.converter,
179179
this.dialect);
180-
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(this.mappingContext, this.converter);
180+
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(this.mappingContext, this.converter, this.dialect, this.operations);
181181
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(this.operations, this.dialect);
182182

183183
DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, this.converter,

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlParametersFactoryTest.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Objects;
2828

2929
import org.junit.jupiter.api.Test;
30+
import org.mockito.Mock;
3031
import org.springframework.core.convert.converter.Converter;
3132
import org.springframework.data.annotation.Id;
3233
import org.springframework.data.convert.ReadingConverter;
@@ -38,19 +39,24 @@
3839
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
3940
import org.springframework.data.relational.core.sql.SqlIdentifier;
4041
import org.springframework.jdbc.core.JdbcOperations;
42+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
43+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
4144

4245
/**
4346
* Unit tests for {@link SqlParametersFactory}.
4447
*
4548
* @author Chirag Tailor
49+
* @author Mikhail Polivakha
4650
*/
4751
class SqlParametersFactoryTest {
4852

4953
RelationalMappingContext context = new JdbcMappingContext();
5054
RelationResolver relationResolver = mock(RelationResolver.class);
5155
MappingJdbcConverter converter = new MappingJdbcConverter(context, relationResolver);
5256
AnsiDialect dialect = AnsiDialect.INSTANCE;
53-
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter);
57+
58+
NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
59+
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect, operations);
5460

5561
@Test // DATAJDBC-412
5662
public void considersConfiguredWriteConverterForIdValueObjects_onRead() {
@@ -301,6 +307,6 @@ private SqlParametersFactory createSqlParametersFactoryWithConverters(List<?> co
301307

302308
MappingJdbcConverter converter = new MappingJdbcConverter(context, relationResolver,
303309
new JdbcCustomConversions(converters), new DefaultJdbcTypeFactory(mock(JdbcOperations.class)));
304-
return new SqlParametersFactory(context, converter);
310+
return new SqlParametersFactory(context, converter, dialect, operations);
305311
}
306312
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

+57-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.springframework.data.relational.core.mapping.Column;
7676
import org.springframework.data.relational.core.mapping.MappedCollection;
7777
import org.springframework.data.relational.core.mapping.Table;
78+
import org.springframework.data.relational.core.mapping.TargetSequence;
7879
import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent;
7980
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
8081
import org.springframework.data.relational.core.sql.LockMode;
@@ -115,8 +116,8 @@ public class JdbcRepositoryIntegrationTests {
115116
@Autowired DummyEntityRepository repository;
116117
@Autowired MyEventListener eventListener;
117118
@Autowired RootRepository rootRepository;
118-
119119
@Autowired WithDelimitedColumnRepository withDelimitedColumnRepository;
120+
@Autowired EntityWithSequenceRepository entityWithSequenceRepository;
120121

121122
@BeforeEach
122123
public void before() {
@@ -135,6 +136,27 @@ public void savesAnEntity() {
135136
"id_Prop = " + entity.getIdProp())).isEqualTo(1);
136137
}
137138

139+
@Test
140+
public void saveEntityWithTargetSequenceSpecified() {
141+
EntityWithSequence first = entityWithSequenceRepository.save(new EntityWithSequence("first"));
142+
EntityWithSequence second = entityWithSequenceRepository.save(new EntityWithSequence("second"));
143+
144+
assertThat(first.getId()).isNotNull();
145+
assertThat(second.getId()).isNotNull();
146+
assertThat(first.getId()).isLessThan(second.getId());
147+
assertThat(first.getName()).isEqualTo("first");
148+
assertThat(second.getName()).isEqualTo("second");
149+
}
150+
151+
@Test
152+
public void batchInsertEntityWithTargetSequenceSpecified() {
153+
Iterable<EntityWithSequence> results = entityWithSequenceRepository.saveAll(
154+
List.of(new EntityWithSequence("first"), new EntityWithSequence("second"))
155+
);
156+
157+
assertThat(results).hasSize(2).extracting(EntityWithSequence::getId).containsExactly(1L, 2L);
158+
}
159+
138160
@Test // DATAJDBC-95
139161
public void saveAndLoadAnEntity() {
140162

@@ -1515,6 +1537,8 @@ interface RootRepository extends ListCrudRepository<Root, Long> {
15151537

15161538
interface WithDelimitedColumnRepository extends CrudRepository<WithDelimitedColumn, Long> {}
15171539

1540+
interface EntityWithSequenceRepository extends CrudRepository<EntityWithSequence, Long> {}
1541+
15181542
@Configuration
15191543
@Import(TestConfiguration.class)
15201544
static class Config {
@@ -1536,6 +1560,11 @@ WithDelimitedColumnRepository withDelimitedColumnRepository() {
15361560
return factory.getRepository(WithDelimitedColumnRepository.class);
15371561
}
15381562

1563+
@Bean
1564+
EntityWithSequenceRepository entityWithSequenceRepository() {
1565+
return factory.getRepository(EntityWithSequenceRepository.class);
1566+
}
1567+
15391568
@Bean
15401569
NamedQueries namedQueries() throws IOException {
15411570

@@ -1839,6 +1868,33 @@ private static DummyEntity createEntity(String entityName, Consumer<DummyEntity>
18391868
return entity;
18401869
}
18411870

1871+
static class EntityWithSequence {
1872+
1873+
@Id
1874+
@Column(value = "entity_id")
1875+
@TargetSequence(sequence = "entity_sequence")
1876+
private Long id;
1877+
1878+
private String name;
1879+
1880+
public EntityWithSequence(Long id, String name) {
1881+
this.id = id;
1882+
this.name = name;
1883+
}
1884+
1885+
public EntityWithSequence(String name) {
1886+
this.name = name;
1887+
}
1888+
1889+
public Long getId() {
1890+
return id;
1891+
}
1892+
1893+
public String getName() {
1894+
return name;
1895+
}
1896+
}
1897+
18421898
static class DummyEntity {
18431899

18441900
String name;

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ void before() {
9191
JdbcConverter converter = new MappingJdbcConverter(context, delegatingDataAccessStrategy,
9292
new JdbcCustomConversions(), new DefaultJdbcTypeFactory(operations.getJdbcOperations()));
9393
SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, converter, dialect);
94-
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter);
94+
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect, operations);
9595
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations, dialect);
9696

9797
this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations,

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ DataAccessStrategy defaultDataAccessStrategy(
167167
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template,
168168
RelationalMappingContext context, JdbcConverter converter, Dialect dialect) {
169169
return new DataAccessStrategyFactory(new SqlGeneratorSource(context, converter, dialect), converter, template,
170-
new SqlParametersFactory(context, converter),
170+
new SqlParametersFactory(context, converter, dialect, template),
171171
new InsertStrategyFactory(template, dialect)).create();
172172
}
173173

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ DataAccessStrategy defaultDataAccessStrategy(
109109
JdbcConverter converter, Dialect dialect) {
110110

111111
return new DataAccessStrategyFactory(new SqlGeneratorSource(context, converter, dialect), converter,
112-
template, new SqlParametersFactory(context, converter),
112+
template, new SqlParametersFactory(context, converter, dialect, template),
113113
new InsertStrategyFactory(template, dialect)).create();
114114
}
115115

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ DROP TABLE ROOT;
33
DROP TABLE INTERMEDIATE;
44
DROP TABLE LEAF;
55
DROP TABLE WITH_DELIMITED_COLUMN;
6+
DROP TABLE ENTITY_WITH_SEQUENCE;
7+
DROP SEQUENCE ENTITY_SEQUENCE;
68

79
CREATE TABLE dummy_entity
810
(
@@ -45,4 +47,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
4547
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
4648
"ORG.XTUNIT.IDENTIFIER" VARCHAR(100),
4749
STYPE VARCHAR(100)
48-
);
50+
);
51+
52+
CREATE TABLE ENTITY_WITH_SEQUENCE
53+
(
54+
ID BIGINT,
55+
NAME VARCHAR(100)
56+
);
57+
58+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql

+9-1
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
3939
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
4040
"ORG.XTUNIT.IDENTIFIER" VARCHAR(100),
4141
STYPE VARCHAR(100)
42-
);
42+
);
43+
44+
CREATE TABLE ENTITY_WITH_SEQUENCE
45+
(
46+
ID BIGINT,
47+
NAME VARCHAR(100)
48+
);
49+
50+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql

+9-1
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
3939
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
4040
"ORG.XTUNIT.IDENTIFIER" VARCHAR(100),
4141
STYPE VARCHAR(100)
42-
);
42+
);
43+
44+
CREATE TABLE ENTITY_WITH_SEQUENCE
45+
(
46+
ID BIGINT,
47+
NAME VARCHAR(100)
48+
);
49+
50+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql

+9-1
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
3939
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
4040
`ORG.XTUNIT.IDENTIFIER` VARCHAR(100),
4141
STYPE VARCHAR(100)
42-
);
42+
);
43+
44+
CREATE TABLE ENTITY_WITH_SEQUENCE
45+
(
46+
ID BIGINT,
47+
NAME VARCHAR(100)
48+
);
49+
50+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ DROP TABLE IF EXISTS ROOT;
33
DROP TABLE IF EXISTS INTERMEDIATE;
44
DROP TABLE IF EXISTS LEAF;
55
DROP TABLE IF EXISTS WITH_DELIMITED_COLUMN;
6+
DROP TABLE IF EXISTS ENTITY_WITH_SEQUENCE;
7+
DROP SEQUENCE IF EXISTS ENTITY_SEQUENCE;
68

79
CREATE TABLE dummy_entity
810
(
@@ -45,4 +47,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
4547
ID BIGINT IDENTITY PRIMARY KEY,
4648
"ORG.XTUNIT.IDENTIFIER" VARCHAR(100),
4749
STYPE VARCHAR(100)
48-
);
50+
);
51+
52+
CREATE TABLE ENTITY_WITH_SEQUENCE
53+
(
54+
ID BIGINT,
55+
NAME VARCHAR(100)
56+
);
57+
58+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql

+9-1
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
4242
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
4343
`ORG.XTUNIT.IDENTIFIER` VARCHAR(100),
4444
STYPE VARCHAR(100)
45-
);
45+
);
46+
47+
CREATE TABLE ENTITY_WITH_SEQUENCE
48+
(
49+
ID BIGINT,
50+
NAME VARCHAR(100)
51+
);
52+
53+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ DROP TABLE ROOT CASCADE CONSTRAINTS PURGE;
33
DROP TABLE INTERMEDIATE CASCADE CONSTRAINTS PURGE;
44
DROP TABLE LEAF CASCADE CONSTRAINTS PURGE;
55
DROP TABLE WITH_DELIMITED_COLUMN CASCADE CONSTRAINTS PURGE;
6+
DROP TABLE ENTITY_WITH_SEQUENCE CASCADE CONSTRAINTS PURGE;
7+
DROP SEQUENCE ENTITY_SEQUENCE;
68

79
CREATE TABLE DUMMY_ENTITY
810
(
@@ -46,3 +48,11 @@ CREATE TABLE WITH_DELIMITED_COLUMN
4648
"ORG.XTUNIT.IDENTIFIER" VARCHAR(100),
4749
STYPE VARCHAR(100)
4850
)
51+
52+
CREATE TABLE ENTITY_WITH_SEQUENCE
53+
(
54+
ID BIGINT,
55+
NAME VARCHAR(100)
56+
);
57+
58+
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;

0 commit comments

Comments
 (0)