From 709d6ef4d5e7e964d62f75408322da80820a161b Mon Sep 17 00:00:00 2001 From: Oleksandr Kucher Date: Sat, 26 Jan 2019 12:01:36 +0200 Subject: [PATCH] DATAJDBC-324 - Add support of read-only columns. Columns annotated with `ReadOnlyProperty` will be ignored when generating INSERT or UPDATE statements. Original pull request: #112. --- .../data/jdbc/core/SqlGenerator.java | 9 ++ .../data/jdbc/core/SqlGeneratorUnitTests.java | 89 +++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java index 3dbadfccd..dc2a8f118 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -24,6 +25,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.springframework.data.annotation.ReadOnlyProperty; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PropertyHandler; @@ -41,6 +43,7 @@ import org.springframework.util.Assert; * @author Jens Schauder * @author Yoichi Imai * @author Bastian Wilhelm + * @author Oleksandr Kucher */ class SqlGenerator { @@ -48,6 +51,7 @@ class SqlGenerator { private final RelationalMappingContext context; private final List columnNames = new ArrayList<>(); private final List nonIdColumnNames = new ArrayList<>(); + private final Set readOnlyColumnNames = new HashSet<>(); private final Lazy findOneSql = Lazy.of(this::createFindOneSelectSql); private final Lazy findAllSql = Lazy.of(this::createFindAllSql); @@ -93,6 +97,9 @@ class SqlGenerator { if (!entity.isIdProperty(property)) { nonIdColumnNames.add(columnName); } + if (property.isAnnotationPresent(ReadOnlyProperty.class)) { + readOnlyColumnNames.add(columnName); + } } private void initEmbeddedColumnNames(RelationalPersistentProperty property, String prefix) { @@ -328,6 +335,7 @@ class SqlGenerator { LinkedHashSet columnNamesForInsert = new LinkedHashSet<>(nonIdColumnNames); columnNamesForInsert.addAll(additionalColumns); + columnNamesForInsert.removeIf(readOnlyColumnNames::contains); String tableColumns = String.join(", ", columnNamesForInsert); @@ -344,6 +352,7 @@ class SqlGenerator { String setClause = columnNames.stream() // .filter(s -> !s.equals(entity.getIdColumn())) // + .filter(s -> !readOnlyColumnNames.contains(s)) // .map(n -> String.format("%s = :%s", n, n)) // .collect(Collectors.joining(", ")); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java index 57517c86d..9ba22ac0f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java @@ -25,6 +25,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.ReadOnlyProperty; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; @@ -40,6 +41,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp * * @author Jens Schauder * @author Greg Turnquist + * @author Oleksandr Kucher */ public class SqlGeneratorUnitTests { @@ -226,6 +228,87 @@ public class SqlGeneratorUnitTests { "id1 = :id"); } + @Test // DATAJDBC-324 + public void readOnlyPropertyExcludedFromQuery_when_generateUpdateSql() { + + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + + assertThat(sqlGenerator.getUpdate()).isEqualToIgnoringCase( // + "UPDATE entity_with_read_only_property " // + + "SET x_name = :x_name " // + + "WHERE x_id = :x_id" // + ); + } + + @Test // DATAJDBC-324 + public void readOnlyPropertyExcludedFromQuery_when_generateInsertSql() { + + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + + assertThat(sqlGenerator.getInsert(emptySet())).isEqualToIgnoringCase( // + "INSERT INTO entity_with_read_only_property (x_name) " // + + "VALUES (:x_name)" // + ); + } + + @Test // DATAJDBC-324 + public void readOnlyPropertyIncludedIntoQuery_when_generateFindAllSql() { + + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + + assertThat(sqlGenerator.getFindAll()).isEqualToIgnoringCase("SELECT " + + "entity_with_read_only_property.x_id AS x_id, " + "entity_with_read_only_property.x_name AS x_name, " + + "entity_with_read_only_property.x_read_only_value AS x_read_only_value " + + "FROM entity_with_read_only_property"); + } + + @Test // DATAJDBC-324 + public void readOnlyPropertyIncludedIntoQuery_when_generateFindAllByPropertySql() { + + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + + assertThat(sqlGenerator.getFindAllByProperty("back-ref", "key-column", true)).isEqualToIgnoringCase( // + "SELECT " // + + "entity_with_read_only_property.x_id AS x_id, " // + + "entity_with_read_only_property.x_name AS x_name, " // + + "entity_with_read_only_property.x_read_only_value AS x_read_only_value, " // + + "entity_with_read_only_property.key-column AS key-column " // + + "FROM entity_with_read_only_property " // + + "WHERE back-ref = :back-ref " // + + "ORDER BY key-column" // + ); + } + + @Test // DATAJDBC-324 + public void readOnlyPropertyIncludedIntoQuery_when_generateFindAllInListSql() { + + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + + assertThat(sqlGenerator.getFindAllInList()).isEqualToIgnoringCase( // + "SELECT " // + + "entity_with_read_only_property.x_id AS x_id, " // + + "entity_with_read_only_property.x_name AS x_name, " // + + "entity_with_read_only_property.x_read_only_value AS x_read_only_value " // + + "FROM entity_with_read_only_property " // + + "WHERE entity_with_read_only_property.x_id in(:ids)" // + ); + } + + @Test // DATAJDBC-324 + public void readOnlyPropertyIncludedIntoQuery_when_generateFindOneSql() { + + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + + assertThat(sqlGenerator.getFindOne()).isEqualToIgnoringCase( // + "SELECT " // + + "entity_with_read_only_property.x_id AS x_id, " // + + "entity_with_read_only_property.x_name AS x_name, " // + + "entity_with_read_only_property.x_read_only_value AS x_read_only_value " // + + "FROM entity_with_read_only_property " // + + "WHERE entity_with_read_only_property.x_id = :id" // + ); + } + private PersistentPropertyPath getPath(String path, Class base) { return PersistentPropertyPathTestUtils.getPath(context, path, base); } @@ -289,4 +372,10 @@ public class SqlGeneratorUnitTests { @Id Long id; } + static class EntityWithReadOnlyProperty { + + @Id Long id; + String name; + @ReadOnlyProperty String readOnlyValue; + } }