Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fb8ef0f

Browse files
schaudermp911de
authored andcommittedNov 20, 2023
Support for Id generation in Oracle using quoted identifiers.
The latest Oracle JDBC driver properly supports returning of generated ids, both in batches and for quoted identifiers. This allows us to now support this feature. Closes #1666 Original pull request: #1667
1 parent cfb22f0 commit fb8ef0f

File tree

10 files changed

+35
-48
lines changed

10 files changed

+35
-48
lines changed
 

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) {
6464
IdGeneration idGeneration = dialect.getIdGeneration();
6565
if (idGeneration.driverRequiresKeyColumnNames()) {
6666

67-
String[] keyColumnNames = getKeyColumnNames();
67+
String[] keyColumnNames = getKeyColumnNames(idGeneration);
6868
if (keyColumnNames.length == 0) {
6969
batchJdbcOperations.batchUpdate(sql, sqlParameterSources, holder);
7070
} else {
@@ -91,10 +91,10 @@ public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) {
9191
return ids;
9292
}
9393

94-
private String[] getKeyColumnNames() {
94+
private String[] getKeyColumnNames(IdGeneration idGeneration) {
9595

9696
return Optional.ofNullable(idColumn)
97-
.map(idColumn -> new String[] { idColumn.getReference() })
97+
.map(idColumn -> new String[] {idGeneration.getKeyColumnName( idColumn) })
9898
.orElse(new String[0]);
9999
}
100100
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public Object execute(String sql, SqlParameterSource sqlParameterSource) {
5858

5959
if (idGeneration.driverRequiresKeyColumnNames()) {
6060

61-
String[] keyColumnNames = getKeyColumnNames();
61+
String[] keyColumnNames = getKeyColumnNames(idGeneration);
6262
if (keyColumnNames.length == 0) {
6363
jdbcOperations.update(sql, sqlParameterSource, holder);
6464
} else {
@@ -84,8 +84,8 @@ public Object execute(String sql, SqlParameterSource sqlParameterSource) {
8484
}
8585
}
8686

87-
private String[] getKeyColumnNames() {
88-
return Optional.ofNullable(idColumn).map(idColumn -> new String[] { idColumn.getReference() })
87+
private String[] getKeyColumnNames(IdGeneration idGeneration) {
88+
return Optional.ofNullable(idColumn).map(idColumn -> new String[] { idGeneration.getKeyColumnName(idColumn) })
8989
.orElse(new String[0]);
9090
}
9191
}

‎spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ private static LegoSet createLegoSet(String name) {
198198
}
199199

200200
@Test // DATAJDBC-112
201-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
202201
void saveAndLoadAnEntityWithReferencedEntityById() {
203202

204203
template.save(legoSet);
@@ -219,7 +218,6 @@ void saveAndLoadAnEntityWithReferencedEntityById() {
219218
}
220219

221220
@Test // DATAJDBC-112
222-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
223221
void saveAndLoadManyEntitiesWithReferencedEntity() {
224222

225223
template.save(legoSet);
@@ -232,7 +230,6 @@ void saveAndLoadManyEntitiesWithReferencedEntity() {
232230
}
233231

234232
@Test // DATAJDBC-101
235-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
236233
void saveAndLoadManyEntitiesWithReferencedEntitySorted() {
237234

238235
template.save(createLegoSet("Lava"));
@@ -247,7 +244,6 @@ void saveAndLoadManyEntitiesWithReferencedEntitySorted() {
247244
}
248245

249246
@Test // DATAJDBC-101
250-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
251247
void saveAndLoadManyEntitiesWithReferencedEntitySortedAndPaged() {
252248

253249
template.save(createLegoSet("Lava"));
@@ -262,7 +258,7 @@ void saveAndLoadManyEntitiesWithReferencedEntitySortedAndPaged() {
262258
}
263259

264260
@Test // GH-821
265-
@EnabledOnFeature({ SUPPORTS_QUOTED_IDS, SUPPORTS_NULL_PRECEDENCE })
261+
@EnabledOnFeature(SUPPORTS_NULL_PRECEDENCE)
266262
void saveAndLoadManyEntitiesWithReferencedEntitySortedWithNullPrecedence() {
267263

268264
template.save(createLegoSet(null));
@@ -279,7 +275,6 @@ void saveAndLoadManyEntitiesWithReferencedEntitySortedWithNullPrecedence() {
279275

280276

281277
@Test //
282-
@EnabledOnFeature({ SUPPORTS_QUOTED_IDS})
283278
void findByNonPropertySortFails() {
284279

285280
assertThatThrownBy(() -> template.findAll(LegoSet.class,
@@ -289,7 +284,6 @@ void findByNonPropertySortFails() {
289284

290285

291286
@Test // DATAJDBC-112
292-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
293287
void saveAndLoadManyEntitiesByIdWithReferencedEntity() {
294288

295289
template.save(legoSet);
@@ -301,7 +295,6 @@ void saveAndLoadManyEntitiesByIdWithReferencedEntity() {
301295
}
302296

303297
@Test // DATAJDBC-112
304-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
305298
void saveAndLoadAnEntityWithReferencedNullEntity() {
306299

307300
legoSet.setManual(null);
@@ -314,7 +307,6 @@ void saveAndLoadAnEntityWithReferencedNullEntity() {
314307
}
315308

316309
@Test // DATAJDBC-112
317-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
318310
void saveAndDeleteAnEntityWithReferencedEntity() {
319311

320312
template.save(legoSet);
@@ -329,7 +321,6 @@ void saveAndDeleteAnEntityWithReferencedEntity() {
329321
}
330322

331323
@Test // DATAJDBC-112
332-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
333324
void saveAndDeleteAllWithReferencedEntity() {
334325

335326
template.save(legoSet);
@@ -345,7 +336,6 @@ void saveAndDeleteAllWithReferencedEntity() {
345336
}
346337

347338
@Test // GH-537
348-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
349339
void saveAndDeleteAllByAggregateRootsWithReferencedEntity() {
350340

351341
LegoSet legoSet1 = template.save(legoSet);
@@ -362,7 +352,6 @@ void saveAndDeleteAllByAggregateRootsWithReferencedEntity() {
362352
}
363353

364354
@Test // GH-537
365-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
366355
void saveAndDeleteAllByIdsWithReferencedEntity() {
367356

368357
LegoSet legoSet1 = template.save(legoSet);
@@ -423,7 +412,7 @@ void insertAndUpdateAllByAggregateRootsWithVersion() {
423412
}
424413

425414
@Test // DATAJDBC-112
426-
@EnabledOnFeature({ SUPPORTS_QUOTED_IDS, SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES })
415+
@EnabledOnFeature(SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES)
427416
void updateReferencedEntityFromNull() {
428417

429418
legoSet.setManual(null);
@@ -442,7 +431,6 @@ void updateReferencedEntityFromNull() {
442431
}
443432

444433
@Test // DATAJDBC-112
445-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
446434
void updateReferencedEntityToNull() {
447435

448436
template.save(legoSet);
@@ -473,7 +461,6 @@ void updateFailedRootDoesNotExist() {
473461
}
474462

475463
@Test // DATAJDBC-112
476-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
477464
void replaceReferencedEntity() {
478465

479466
template.save(legoSet);
@@ -494,7 +481,7 @@ void replaceReferencedEntity() {
494481
}
495482

496483
@Test // DATAJDBC-112
497-
@EnabledOnFeature({ SUPPORTS_QUOTED_IDS, TestDatabaseFeatures.Feature.SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES })
484+
@EnabledOnFeature(TestDatabaseFeatures.Feature.SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES)
498485
void changeReferencedEntity() {
499486

500487
template.save(legoSet);
@@ -509,7 +496,6 @@ void changeReferencedEntity() {
509496
}
510497

511498
@Test // DATAJDBC-266
512-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
513499
void oneToOneChildWithoutId() {
514500

515501
OneToOneParent parent = new OneToOneParent();
@@ -526,7 +512,6 @@ void oneToOneChildWithoutId() {
526512
}
527513

528514
@Test // DATAJDBC-266
529-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
530515
void oneToOneNullChildWithoutId() {
531516

532517
OneToOneParent parent = new OneToOneParent();
@@ -542,7 +527,6 @@ void oneToOneNullChildWithoutId() {
542527
}
543528

544529
@Test // DATAJDBC-266
545-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
546530
void oneToOneNullAttributes() {
547531

548532
OneToOneParent parent = new OneToOneParent();
@@ -558,7 +542,6 @@ void oneToOneNullAttributes() {
558542
}
559543

560544
@Test // DATAJDBC-125
561-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
562545
void saveAndLoadAnEntityWithSecondaryReferenceNull() {
563546

564547
template.save(legoSet);
@@ -571,7 +554,6 @@ void saveAndLoadAnEntityWithSecondaryReferenceNull() {
571554
}
572555

573556
@Test // DATAJDBC-125
574-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
575557
void saveAndLoadAnEntityWithSecondaryReferenceNotNull() {
576558

577559
legoSet.alternativeInstructions = new Manual();
@@ -593,7 +575,6 @@ void saveAndLoadAnEntityWithSecondaryReferenceNotNull() {
593575
}
594576

595577
@Test // DATAJDBC-276
596-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
597578
void saveAndLoadAnEntityWithListOfElementsWithoutId() {
598579

599580
ListParent entity = new ListParent();
@@ -612,7 +593,6 @@ void saveAndLoadAnEntityWithListOfElementsWithoutId() {
612593
}
613594

614595
@Test // GH-498 DATAJDBC-273
615-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
616596
void saveAndLoadAnEntityWithListOfElementsInConstructor() {
617597

618598
ElementNoId element = new ElementNoId();
@@ -752,7 +732,6 @@ void saveAndLoadAnEntityWithByteArray() {
752732
}
753733

754734
@Test // DATAJDBC-340
755-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
756735
void saveAndLoadLongChain() {
757736

758737
Chain4 chain4 = new Chain4();
@@ -781,7 +760,6 @@ void saveAndLoadLongChain() {
781760
}
782761

783762
@Test // DATAJDBC-359
784-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
785763
void saveAndLoadLongChainWithoutIds() {
786764

787765
NoIdChain4 chain4 = new NoIdChain4();
@@ -1012,7 +990,6 @@ void saveAndUpdateAggregateWithIdAndNullVersion() {
1012990
}
1013991

1014992
@Test // DATAJDBC-462
1015-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
1016993
void resavingAnUnversionedEntity() {
1017994

1018995
LegoSet legoSet = new LegoSet();

‎spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ public class JdbcAggregateTemplateSchemaIntegrationTests {
5555
@Autowired NamedParameterJdbcOperations jdbcTemplate;
5656

5757
@Test
58-
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
5958
public void insertFindUpdateDelete() {
6059

6160
DummyEntity entity = new DummyEntity();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
/**
3232
* Base class to test <code>@EnableJdbcRepositories(queryLookupStrategy = ...)</code>
33-
*
33+
*
3434
* @author Diego Krupitza
3535
* @since 2.4
3636
*/

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public TestDatabaseFeatures(JdbcOperations jdbcTemplate) {
4040
String productName = jdbcTemplate.execute(
4141
(ConnectionCallback<String>) c -> c.getMetaData().getDatabaseProductName().toLowerCase(Locale.ENGLISH));
4242

43-
database = Arrays.stream(Database.values()).filter(db -> db.matches(productName)).findFirst().get();
43+
database = Arrays.stream(Database.values()).filter(db -> db.matches(productName)).findFirst().orElseThrow();
4444
}
4545

4646
/**
@@ -50,15 +50,6 @@ private void supportsHugeNumbers() {
5050
assumeThat(database).isNotIn(Database.Oracle, Database.SqlServer);
5151
}
5252

53-
/**
54-
* Oracles JDBC driver seems to have a bug that makes it impossible to acquire generated keys when the column is
55-
* quoted. See
56-
* https://stackoverflow.com/questions/62263576/how-to-get-the-generated-key-for-a-column-with-lowercase-characters-from-oracle
57-
*/
58-
private void supportsQuotedIds() {
59-
assumeThat(database).isNotEqualTo(Database.Oracle);
60-
}
61-
6253
/**
6354
* Microsoft SqlServer does not allow explicitly setting ids in columns where the value gets generated by the
6455
* database. Such columns therefore must not be used in referenced entities, since we do a delete and insert, which
@@ -115,7 +106,6 @@ boolean matches(String productName) {
115106
public enum Feature {
116107

117108
SUPPORTS_MULTIDIMENSIONAL_ARRAYS(TestDatabaseFeatures::supportsMultiDimensionalArrays), //
118-
SUPPORTS_QUOTED_IDS(TestDatabaseFeatures::supportsQuotedIds), //
119109
SUPPORTS_HUGE_NUMBERS(TestDatabaseFeatures::supportsHugeNumbers), //
120110
SUPPORTS_ARRAYS(TestDatabaseFeatures::supportsArrays), //
121111
SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES(TestDatabaseFeatures::supportsGeneratedIdsInReferencedEntities), //

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-oracle.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ CREATE TABLE MANUAL
4343
(
4444
"id2" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY,
4545
LEGO_SET NUMBER,
46-
ALTERNATIVE NUMBER,
46+
"alternative" NUMBER,
4747
CONTENT VARCHAR(2000)
4848
);
4949

@@ -59,7 +59,7 @@ CREATE TABLE ONE_TO_ONE_PARENT
5959
CREATE TABLE Child_No_Id
6060
(
6161
ONE_TO_ONE_PARENT INTEGER PRIMARY KEY,
62-
"content" VARCHAR(30)
62+
CONTENT VARCHAR(30)
6363
);
6464

6565
CREATE TABLE LIST_PARENT

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-oracle.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
DROP USER OTHER CASCADE;
22

3-
43
CREATE USER OTHER;
54

5+
ALTER USER OTHER QUOTA UNLIMITED ON USERS;
6+
67
CREATE TABLE OTHER.DUMMY_ENTITY
78
(
89
ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY,

‎spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/IdGeneration.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.sql.Connection;
1919
import java.sql.PreparedStatement;
2020

21+
import org.springframework.data.relational.core.sql.SqlIdentifier;
22+
2123
/**
2224
* Describes how obtaining generated ids after an insert works for a given JDBC driver.
2325
*
@@ -45,6 +47,18 @@ default boolean driverRequiresKeyColumnNames() {
4547
return false;
4648
}
4749

50+
/**
51+
* Provides for a given id {@link SqlIdentifier} the String that is to be used for registering interest in the
52+
* generated value of that column.
53+
*
54+
* @param id {@link SqlIdentifier} representing a column for which a generated value is to be obtained.
55+
* @return a String representing that column in the way expected by the JDBC driver.
56+
* @since 3.3
57+
*/
58+
default String getKeyColumnName(SqlIdentifier id) {
59+
return id.getReference();
60+
}
61+
4862
/**
4963
* Does the driver support id generation for batch operations.
5064
* <p>

‎spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.springframework.core.convert.converter.Converter;
1919
import org.springframework.data.convert.ReadingConverter;
2020
import org.springframework.data.convert.WritingConverter;
21+
import org.springframework.data.relational.core.sql.SqlIdentifier;
2122

2223
import java.util.Collection;
2324
import java.util.Collections;
@@ -42,6 +43,11 @@ public class OracleDialect extends AnsiDialect {
4243
public boolean driverRequiresKeyColumnNames() {
4344
return true;
4445
}
46+
47+
@Override
48+
public String getKeyColumnName(SqlIdentifier id) {
49+
return id.toSql(INSTANCE.getIdentifierProcessing());
50+
}
4551
};
4652

4753
protected OracleDialect() {}

0 commit comments

Comments
 (0)
Please sign in to comment.