Skip to content

Commit 1f7f2d2

Browse files
authored
Merge branch 'spring-projects:main' into main
2 parents 7f06887 + e6fe49d commit 1f7f2d2

File tree

56 files changed

+1347
-483
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1347
-483
lines changed

.github/workflows/codeql.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141

4242
# Initializes the CodeQL tools for scanning.
4343
- name: Initialize CodeQL
44-
uses: github/codeql-action/init@v2
44+
uses: github/codeql-action/init@v3
4545
with:
4646
languages: ${{ matrix.language }}
4747
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -55,7 +55,7 @@ jobs:
5555
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
5656
# If this step fails, then you should remove it and run the build manually (see below)
5757
- name: Autobuild
58-
uses: github/codeql-action/autobuild@v2
58+
uses: github/codeql-action/autobuild@v3
5959

6060
# ℹ️ Command-line programs to run using the OS shell.
6161
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -68,6 +68,6 @@ jobs:
6868
# ./location_of_script_within_repo/buildscript.sh
6969

7070
- name: Perform CodeQL Analysis
71-
uses: github/codeql-action/analyze@v2
71+
uses: github/codeql-action/analyze@v3
7272
with:
7373
category: "/language:${{matrix.language}}"

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ target/
1111
*.graphml
1212
package-lock.json
1313
.mvn/.develocity
14+
.mvn/maven.config
1415

1516
build/
1617
node_modules

README.adoc

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= Spring Data Relational image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jdbc%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jdbc/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data Relational Parent"]
1+
= Spring Data Relational image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jdbc%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jdbc/] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data Relational Parent"]
22

33
The primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
44

@@ -202,7 +202,6 @@ https://docs.spring.io/spring-data/relational/reference/[reference documentation
202202
If you are just starting out with Spring, try one of the https://spring.io/guides[guides].
203203
* If you are upgrading, check out the https://github.com/spring-projects/spring-data-relational/releases[changelog] for "`new and noteworthy`" features.
204204
* Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data`].
205-
You can also chat with the community on https://gitter.im/spring-projects/spring-data[Gitter].
206205

207206
== Reporting Issues
208207

pom.xml

+5-6
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,21 @@
3535
<h2.version>2.3.232</h2.version>
3636
<hikari.version>5.1.0</hikari.version>
3737
<hsqldb.version>2.7.3</hsqldb.version>
38-
<mariadb-java-client.version>3.5.1</mariadb-java-client.version>
39-
<mssql.version>12.8.1.jre11</mssql.version>
40-
<mysql-connector-java.version>8.0.33</mysql-connector-java.version>
41-
<postgresql.version>42.7.4</postgresql.version>
38+
<mariadb-java-client.version>3.5.3</mariadb-java-client.version>
39+
<mssql.version>12.10.0.jre11</mssql.version>
40+
<mysql-connector-java.version>9.2.0</mysql-connector-java.version>
41+
<postgresql.version>42.7.5</postgresql.version>
4242
<oracle.version>23.7.0.25.01</oracle.version>
4343

4444
<!-- R2DBC driver dependencies-->
4545
<r2dbc-postgresql.version>1.0.7.RELEASE</r2dbc-postgresql.version>
4646
<r2dbc-h2.version>1.0.0.RELEASE</r2dbc-h2.version>
4747
<r2dbc-mariadb.version>1.1.4</r2dbc-mariadb.version>
4848
<r2dbc-mssql.version>1.0.2.RELEASE</r2dbc-mssql.version>
49-
<r2dbc-mysql.version>1.3.2</r2dbc-mysql.version>
49+
<r2dbc-mysql.version>1.4.1</r2dbc-mysql.version>
5050
<oracle-r2dbc.version>1.3.0</oracle-r2dbc.version>
5151

5252
<!-- test dependencies -->
53-
<awaitility.version>4.2.0</awaitility.version>
5453
<archunit.version>1.3.0</archunit.version>
5554

5655
<jmh.version>1.37</jmh.version>

spring-data-jdbc/pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@
124124
</dependency>
125125

126126
<dependency>
127-
<groupId>mysql</groupId>
128-
<artifactId>mysql-connector-java</artifactId>
127+
<groupId>com.mysql</groupId>
128+
<artifactId>mysql-connector-j</artifactId>
129129
<version>${mysql-connector-java.version}</version>
130130
<scope>test</scope>
131131
</dependency>
@@ -170,7 +170,7 @@
170170
<dependency>
171171
<groupId>org.awaitility</groupId>
172172
<artifactId>awaitility</artifactId>
173-
<version>${awaitility.version}</version>
173+
<version>${awaitility}</version>
174174
<scope>test</scope>
175175
</dependency>
176176

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

-1
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,6 @@ private <T> RootAggregateChange<T> createInsertChange(T instance) {
557557
}
558558

559559
private <T> RootAggregateChange<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
560-
561560
RootAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
562561
entityAndVersion.version);
563562
new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity, aggregateChange);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.core.convert;
17+
18+
import org.springframework.data.mapping.PersistentPropertyAccessor;
19+
import org.springframework.data.mapping.context.MappingContext;
20+
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
21+
import org.springframework.data.relational.core.dialect.Dialect;
22+
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
23+
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
24+
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
25+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
26+
import org.springframework.util.Assert;
27+
28+
/**
29+
* Callback for generating identifier values through a database sequence.
30+
*
31+
* @author Mikhail Polivakha
32+
* @author Mark Paluch
33+
* @since 3.5
34+
*/
35+
public class IdGeneratingEntityCallback implements BeforeSaveCallback<Object> {
36+
37+
private final MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> context;
38+
private final SequenceEntityCallbackDelegate delegate;
39+
40+
public IdGeneratingEntityCallback(
41+
MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> context, Dialect dialect,
42+
NamedParameterJdbcOperations operations) {
43+
44+
this.context = context;
45+
this.delegate = new SequenceEntityCallbackDelegate(dialect, operations);
46+
}
47+
48+
@Override
49+
public Object onBeforeSave(Object aggregate, MutableAggregateChange<Object> aggregateChange) {
50+
51+
Assert.notNull(aggregate, "Aggregate must not be null");
52+
53+
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(aggregate.getClass());
54+
55+
if (!entity.hasIdProperty()) {
56+
return aggregate;
57+
}
58+
59+
RelationalPersistentProperty property = entity.getRequiredIdProperty();
60+
PersistentPropertyAccessor<Object> accessor = entity.getPropertyAccessor(aggregate);
61+
62+
if (!entity.isNew(aggregate) || delegate.hasValue(property, accessor) || !property.hasSequence()) {
63+
return aggregate;
64+
}
65+
66+
delegate.generateSequenceValue(property, accessor);
67+
68+
return accessor.getBean();
69+
}
70+
71+
}

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import java.util.Iterator;
2323
import java.util.Map;
2424

25-
import org.slf4j.Logger;
26-
import org.slf4j.LoggerFactory;
25+
import org.apache.commons.logging.Log;
26+
import org.apache.commons.logging.LogFactory;
2727
import org.springframework.dao.DataRetrievalFailureException;
2828
import org.springframework.data.jdbc.core.convert.RowDocumentExtractorSupport.AggregateContext;
2929
import org.springframework.data.jdbc.core.convert.RowDocumentExtractorSupport.RowDocumentSink;
@@ -45,8 +45,8 @@
4545
*/
4646
class RowDocumentResultSetExtractor {
4747

48-
private static final Logger log = LoggerFactory.getLogger(RowDocumentResultSetExtractor.class);
49-
public static final String DUPLICATE_COLUMN_WARNING = "ResultSet contains column \"{}\" multiple times. Later column index is {}";
48+
private static final Log log = LogFactory.getLog(RowDocumentResultSetExtractor.class);
49+
public static final String DUPLICATE_COLUMN_WARNING = "ResultSet contains column \"%s\" multiple times. Later column index is %s";
5050

5151
private final RelationalMappingContext context;
5252
private final PathToColumnMapping propertyToColumn;
@@ -76,7 +76,7 @@ static RowDocument toRowDocument(ResultSet resultSet) throws SQLException {
7676
String columnName = JdbcUtils.lookupColumnName(md, i+1);
7777
Object old = document.putIfAbsent(columnName, rsv instanceof Array a ? a.getArray() : rsv);
7878
if (old != null) {
79-
log.warn(DUPLICATE_COLUMN_WARNING, columnName, i);
79+
log.warn(DUPLICATE_COLUMN_WARNING.formatted(columnName, i));
8080
}
8181
}
8282

@@ -120,7 +120,7 @@ public Map<String, Integer> getColumnMap(ResultSet result) {
120120
String columnLabel = metaData.getColumnLabel(i + 1);
121121
Object old = columns.put(columnLabel, i + 1);
122122
if (old != null) {
123-
log.warn(DUPLICATE_COLUMN_WARNING, columnLabel, i);
123+
log.warn(DUPLICATE_COLUMN_WARNING.formatted( columnLabel, i));
124124
}
125125
}
126126
return columns;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.core.convert;
17+
18+
import org.apache.commons.logging.Log;
19+
import org.apache.commons.logging.LogFactory;
20+
21+
import org.springframework.data.mapping.PersistentProperty;
22+
import org.springframework.data.mapping.PersistentPropertyAccessor;
23+
import org.springframework.data.relational.core.dialect.Dialect;
24+
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
25+
import org.springframework.data.relational.core.sql.SqlIdentifier;
26+
import org.springframework.data.util.ReflectionUtils;
27+
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
28+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
29+
import org.springframework.lang.Nullable;
30+
import org.springframework.util.ClassUtils;
31+
import org.springframework.util.NumberUtils;
32+
33+
/**
34+
* Support class for generating identifier values through a database sequence.
35+
*
36+
* @author Mikhail Polivakha
37+
* @author Mark Paluch
38+
* @since 3.5
39+
* @see org.springframework.data.relational.core.mapping.Sequence
40+
*/
41+
class SequenceEntityCallbackDelegate {
42+
43+
private static final Log LOG = LogFactory.getLog(SequenceEntityCallbackDelegate.class);
44+
private final static MapSqlParameterSource EMPTY_PARAMETERS = new MapSqlParameterSource();
45+
46+
private final Dialect dialect;
47+
private final NamedParameterJdbcOperations operations;
48+
49+
public SequenceEntityCallbackDelegate(Dialect dialect, NamedParameterJdbcOperations operations) {
50+
this.dialect = dialect;
51+
this.operations = operations;
52+
}
53+
54+
@SuppressWarnings("unchecked")
55+
protected void generateSequenceValue(RelationalPersistentProperty property,
56+
PersistentPropertyAccessor<Object> accessor) {
57+
58+
Object sequenceValue = getSequenceValue(property);
59+
60+
if (sequenceValue == null) {
61+
return;
62+
}
63+
64+
Class<?> targetType = ClassUtils.resolvePrimitiveIfNecessary(property.getType());
65+
if (sequenceValue instanceof Number && Number.class.isAssignableFrom(targetType)) {
66+
sequenceValue = NumberUtils.convertNumberToTargetClass((Number) sequenceValue,
67+
(Class<? extends Number>) targetType);
68+
}
69+
70+
accessor.setProperty(property, sequenceValue);
71+
}
72+
73+
protected boolean hasValue(PersistentProperty<?> property, PersistentPropertyAccessor<Object> propertyAccessor) {
74+
75+
Object identifier = propertyAccessor.getProperty(property);
76+
77+
if (property.getType().isPrimitive()) {
78+
79+
Object primitiveDefault = ReflectionUtils.getPrimitiveDefault(property.getType());
80+
return !primitiveDefault.equals(identifier);
81+
}
82+
83+
return identifier != null;
84+
}
85+
86+
private @Nullable Object getSequenceValue(RelationalPersistentProperty property) {
87+
88+
SqlIdentifier sequence = property.getSequence();
89+
90+
if (sequence != null && !dialect.getIdGeneration().sequencesSupported()) {
91+
LOG.warn("""
92+
Aggregate type '%s' is marked for sequence usage but configured dialect '%s'
93+
does not support sequences. Falling back to identity columns.
94+
""".formatted(property.getOwner().getType(), ClassUtils.getQualifiedName(dialect.getClass())));
95+
return null;
96+
}
97+
98+
String sql = dialect.getIdGeneration().createSequenceQuery(sequence);
99+
return operations.queryForObject(sql, EMPTY_PARAMETERS, (rs, rowNum) -> rs.getObject(1));
100+
}
101+
102+
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java

-74
This file was deleted.

0 commit comments

Comments
 (0)