Skip to content

Commit 3d200b3

Browse files
committed
Introduce @InsertOnlyProperty.
For Spring Data JDBC you may now annotate properties of the aggregate root with `@InsertOnlyProperty`. Properties annotated in such way will be written to the database only during insert operations, but they will not be updated afterwards. Closes #637
1 parent 856cac0 commit 3d200b3

File tree

8 files changed

+84
-4
lines changed

8 files changed

+84
-4
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,7 @@ static class Columns {
10241024
private final List<SqlIdentifier> idColumnNames = new ArrayList<>();
10251025
private final List<SqlIdentifier> nonIdColumnNames = new ArrayList<>();
10261026
private final Set<SqlIdentifier> readOnlyColumnNames = new HashSet<>();
1027+
private final Set<SqlIdentifier> insertOnlyColumnNames = new HashSet<>();
10271028
private final Set<SqlIdentifier> insertableColumns;
10281029
private final Set<SqlIdentifier> updatableColumns;
10291030

@@ -1045,6 +1046,7 @@ static class Columns {
10451046

10461047
updatable.removeAll(idColumnNames);
10471048
updatable.removeAll(readOnlyColumnNames);
1049+
updatable.removeAll(insertOnlyColumnNames);
10481050

10491051
this.updatableColumns = Collections.unmodifiableSet(updatable);
10501052
}
@@ -1077,6 +1079,10 @@ private void initSimpleColumnName(RelationalPersistentProperty property, String
10771079
if (!property.isWritable()) {
10781080
readOnlyColumnNames.add(columnName);
10791081
}
1082+
1083+
if (property.isInsertOnly()) {
1084+
insertOnlyColumnNames.add(columnName);
1085+
}
10801086
}
10811087

10821088
private void initEmbeddedColumnNames(RelationalPersistentProperty property, String prefix) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ <T> SqlIdentifierParameterSource forInsert(T instance, Class<T> domainType, Iden
9595
*/
9696
<T> SqlIdentifierParameterSource forUpdate(T instance, Class<T> domainType) {
9797

98-
return getParameterSource(instance, getRequiredPersistentEntity(domainType), "", Predicates.includeAll(),
98+
return getParameterSource(instance, getRequiredPersistentEntity(domainType), "", RelationalPersistentProperty::isInsertOnly,
9999
dialect.getIdentifierProcessing());
100100
}
101101

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

+22-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
6565
import org.springframework.data.relational.core.conversion.DbActionExecutionException;
6666
import org.springframework.data.relational.core.mapping.Column;
67+
import org.springframework.data.relational.core.mapping.InsertOnlyProperty;
6768
import org.springframework.data.relational.core.mapping.MappedCollection;
6869
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
6970
import org.springframework.data.relational.core.mapping.Table;
@@ -1031,6 +1032,20 @@ void updateIdOnlyAggregate() {
10311032
template.save(entity);
10321033
}
10331034

1035+
@Test // GH-637
1036+
void insertOnlyPropertyDoesNotGetUpdated() {
1037+
1038+
WithInsertOnly entity = new WithInsertOnly();
1039+
entity.insertOnly = "first value";
1040+
1041+
assertThat(template.save(entity).id).isNotNull();
1042+
1043+
entity.insertOnly = "second value";
1044+
template.save(entity);
1045+
1046+
assertThat(template.findById(entity.id, WithInsertOnly.class).insertOnly).isEqualTo("first value");
1047+
}
1048+
10341049
private <T extends Number> void saveAndUpdateAggregateWithVersion(VersionedAggregate aggregate,
10351050
Function<Number, T> toConcreteNumber) {
10361051
saveAndUpdateAggregateWithVersion(aggregate, toConcreteNumber, 0);
@@ -1462,10 +1477,16 @@ static class WithLocalDateTime {
14621477
}
14631478

14641479
@Table
1465-
class WithIdOnly {
1480+
static class WithIdOnly {
14661481
@Id Long id;
14671482
}
14681483

1484+
@Table
1485+
static class WithInsertOnly {
1486+
@Id Long id;
1487+
@InsertOnlyProperty
1488+
String insertOnly;
1489+
}
14691490

14701491
@Configuration
14711492
@Import(TestConfiguration.class)

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

+6
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
325325
TEST_TIME TIMESTAMP(9)
326326
);
327327

328+
CREATE TABLE WITH_INSERT_ONLY
329+
(
330+
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
331+
INSERT_ONLY VARCHAR(100)
332+
);
333+
328334
CREATE TABLE WITH_ID_ONLY
329335
(
330336
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY

spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java

+5
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ public boolean shouldCreateEmptyEmbedded() {
199199
return findAnnotation != null && OnEmpty.USE_EMPTY.equals(findAnnotation.onEmpty());
200200
}
201201

202+
@Override
203+
public boolean isInsertOnly() {
204+
return findAnnotation(InsertOnlyProperty.class) != null;
205+
}
206+
202207
private boolean isListLike() {
203208
return isCollectionLike() && !Set.class.isAssignableFrom(this.getType());
204209
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2022 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.relational.core.mapping;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
@Retention(RetentionPolicy.RUNTIME)
26+
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
27+
@Documented
28+
public @interface InsertOnlyProperty {
29+
}

spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,13 @@ default String getEmbeddedPrefix() {
7373

7474
/**
7575
* Returns whether an empty embedded object is supposed to be created for this property.
76-
*
77-
* @return
7876
*/
7977
boolean shouldCreateEmptyEmbedded();
78+
79+
/**
80+
* Returns whether this property is only to be used during inserts and read.
81+
*
82+
* @since 3.0
83+
*/
84+
boolean isInsertOnly();
8085
}

src/main/asciidoc/jdbc.adoc

+8
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,14 @@ Therefore, you have to reload it explicitly if you want to see data that was gen
435435
If the annotated attribute is an entity or collection of entities, it is represented by one or more separate rows in separate tables.
436436
Spring Data JDBC will not perform any insert, delete or update for these rows.
437437

438+
[[jdbc.entity-persistence.insert-only-properties]]
439+
=== Insert Only Properties
440+
441+
Attributes annotated with `@InsertOnlyProperty` will only be written to the database by Spring Data JDBC during insert operations.
442+
For updates these properties will be ignored.
443+
444+
`@InsertOnlyProperty` is only supported for the aggregate root.
445+
438446
[[jdbc.entity-persistence.optimistic-locking]]
439447
=== Optimistic Locking
440448

0 commit comments

Comments
 (0)