Browse Source

DATAJDBC-107 - Implement naming strategy.

Created NamingStrategy and a default implementation that replicates the current solution. Several unit tests illustrates how to override the default and plugin a custom solution including a ThreadLocal, contextual one that could be user-based if, for example, Spring Security's SecurityContextHolder was used.
pull/13/head
Greg Turnquist 8 years ago committed by Jens Schauder
parent
commit
f7bff01d3e
  1. 2
      src/main/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentProperty.java
  2. 53
      src/main/java/org/springframework/data/jdbc/mapping/model/DefaultNamingStrategy.java
  3. 10
      src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java
  4. 8
      src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntityImpl.java
  5. 33
      src/main/java/org/springframework/data/jdbc/mapping/model/NamingStrategy.java
  6. 13
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
  7. 17
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
  8. 3
      src/test/java/org/springframework/data/jdbc/core/JdbcEntityTemplateIntegrationTests.java
  9. 214
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorContextBasedNamingStrategyUnitTests.java
  10. 201
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorFixedNamingStrategyUnitTests.java
  11. 30
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java
  12. 3
      src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityDeleteWriterUnitTests.java
  13. 3
      src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterUnitTests.java
  14. 6
      src/test/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentPropertyUnitTests.java
  15. 22
      src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java
  16. 3
      src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
  17. 3
      src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

2
src/main/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentProperty.java

@ -74,7 +74,7 @@ public class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProper @@ -74,7 +74,7 @@ public class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProper
* @see org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty#getColumnName()
*/
public String getColumnName() {
return getName();
return this.context.getNamingStrategy().getColumnName(this);
}
/**

53
src/main/java/org/springframework/data/jdbc/mapping/model/DefaultNamingStrategy.java

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.mapping.model;
/**
* Basic implementation of {@link NamingStrategy} with no schema, table based on {@link Class} and
* column name based on {@link JdbcPersistentProperty}.
*
* NOTE: Can also be used as an adapter. Create an anonymous subclass and override any settings to implement
* a different strategy on the fly.
*
* @author Greg Turnquist
*/
public class DefaultNamingStrategy implements NamingStrategy {
/**
* No schema at all!
*/
@Override
public String getSchema() {
return "";
}
/**
* Look up the {@link Class}'s simple name.
*/
@Override
public String getTableName(Class<?> type) {
return type.getSimpleName();
}
/**
* Look up the {@link JdbcPersistentProperty}'s name.
*/
@Override
public String getColumnName(JdbcPersistentProperty property) {
return property.getName();
}
}

10
src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java

@ -17,6 +17,8 @@ package org.springframework.data.jdbc.mapping.model; @@ -17,6 +17,8 @@ package org.springframework.data.jdbc.mapping.model;
import static java.util.Arrays.*;
import lombok.Getter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.temporal.Temporal;
@ -46,7 +48,11 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt @@ -46,7 +48,11 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
Temporal.class //
));
public JdbcMappingContext() {
private final @Getter NamingStrategy namingStrategy;
public JdbcMappingContext(NamingStrategy namingStrategy) {
this.namingStrategy = namingStrategy;
setSimpleTypeHolder(new SimpleTypeHolder(CUSTOM_SIMPLE_TYPES, true));
}
@ -80,7 +86,7 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt @@ -80,7 +86,7 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
*/
@Override
protected <T> JdbcPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
return new JdbcPersistentEntityImpl<>(typeInformation);
return new JdbcPersistentEntityImpl<>(typeInformation, this.namingStrategy);
}
/*

8
src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntityImpl.java

@ -29,6 +29,7 @@ import org.springframework.data.util.TypeInformation; @@ -29,6 +29,7 @@ import org.springframework.data.util.TypeInformation;
class JdbcPersistentEntityImpl<T> extends BasicPersistentEntity<T, JdbcPersistentProperty>
implements JdbcPersistentEntity<T> {
private final NamingStrategy namingStrategy;
private final @Getter String tableName;
/**
@ -36,11 +37,12 @@ class JdbcPersistentEntityImpl<T> extends BasicPersistentEntity<T, JdbcPersisten @@ -36,11 +37,12 @@ class JdbcPersistentEntityImpl<T> extends BasicPersistentEntity<T, JdbcPersisten
*
* @param information must not be {@literal null}.
*/
JdbcPersistentEntityImpl(TypeInformation<T> information) {
JdbcPersistentEntityImpl(TypeInformation<T> information, NamingStrategy namingStrategy) {
super(information);
tableName = getType().getSimpleName();
this.namingStrategy = namingStrategy;
this.tableName = this.namingStrategy.getQualifiedTableName(getType());
}
/*
@ -49,7 +51,7 @@ class JdbcPersistentEntityImpl<T> extends BasicPersistentEntity<T, JdbcPersisten @@ -49,7 +51,7 @@ class JdbcPersistentEntityImpl<T> extends BasicPersistentEntity<T, JdbcPersisten
*/
@Override
public String getIdColumn() {
return getRequiredIdProperty().getName();
return this.namingStrategy.getColumnName(getRequiredIdProperty());
}
@Override

33
src/main/java/org/springframework/data/jdbc/mapping/model/NamingStrategy.java

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.mapping.model;
/**
* @author Greg Turnquist
*/
public interface NamingStrategy {
String getSchema();
String getTableName(Class<?> type);
String getColumnName(JdbcPersistentProperty property);
default String getQualifiedTableName(Class<?> type) {
return this.getSchema() + (this.getSchema().equals("") ? "" : ".") + this.getTableName(type);
}
}

13
src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java

@ -15,14 +15,13 @@ @@ -15,14 +15,13 @@
*/
package org.springframework.data.jdbc.repository.support;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.JdbcEntityTemplate;
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentEntityInformation;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.jdbc.repository.SimpleJdbcRepository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.RepositoryInformation;
@ -34,13 +33,19 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -34,13 +33,19 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
* @author Jens Schauder
* @since 2.0
*/
@RequiredArgsConstructor
public class JdbcRepositoryFactory extends RepositoryFactorySupport {
private final JdbcMappingContext context = new JdbcMappingContext();
private final JdbcMappingContext context;
private final NamedParameterJdbcOperations jdbcOperations;
private final ApplicationEventPublisher publisher;
public JdbcRepositoryFactory(NamedParameterJdbcOperations namedParameterJdbcOperations, ApplicationEventPublisher publisher, NamingStrategy namingStrategy) {
this.jdbcOperations = namedParameterJdbcOperations;
this.publisher = publisher;
this.context = new JdbcMappingContext(namingStrategy);
}
@SuppressWarnings("unchecked")
@Override
public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) {

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

@ -23,6 +23,8 @@ import javax.sql.DataSource; @@ -23,6 +23,8 @@ import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
@ -45,9 +47,12 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -45,9 +47,12 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
"No unique NamedParameterJdbcOperation could be found, " //
+ "nor JdbcOperations or DataSource to construct one from.";
private static final String NO_NAMING_STRATEGY_ERROR_MESSAGE = "No unique NamingStrategy could be found.";
private static final String NAMED_PARAMETER_JDBC_OPERATIONS_BEAN_NAME = "namedParameterJdbcTemplate";
private static final String JDBC_OPERATIONS_BEAN_NAME = "jdbcTemplate";
private static final String DATA_SOURCE_BEAN_NAME = "dataSource";
private static final String NAMING_STRATEGY_BEAN_NAME = "namingStrategy";
private final ApplicationEventPublisher applicationEventPublisher;
private final ApplicationContext context;
@ -62,7 +67,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -62,7 +67,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {
return new JdbcRepositoryFactory(findOrCreateJdbcOperations(), applicationEventPublisher);
return new JdbcRepositoryFactory(findOrCreateJdbcOperations(), applicationEventPublisher, findOrCreateNamingStrategy());
}
private NamedParameterJdbcOperations findOrCreateJdbcOperations() {
@ -75,6 +80,12 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -75,6 +80,12 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
.orElseThrow(() -> new IllegalStateException(NO_NAMED_PARAMETER_JDBC_OPERATION_ERROR_MESSAGE));
}
private NamingStrategy findOrCreateNamingStrategy() {
return getNamingStrategy()
.orElse(new DefaultNamingStrategy());
}
private Optional<NamedParameterJdbcOperations> getNamedParameterJdbcOperations() {
return getBean(NamedParameterJdbcOperations.class, NAMED_PARAMETER_JDBC_OPERATIONS_BEAN_NAME);
}
@ -87,6 +98,10 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -87,6 +98,10 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
return getBean(DataSource.class, DATA_SOURCE_BEAN_NAME);
}
private Optional<NamingStrategy> getNamingStrategy() {
return getBean(NamingStrategy.class, NAMING_STRATEGY_BEAN_NAME);
}
private <R> Optional<R> getBean(Class<R> type, String name) {
Map<String, R> beansOfType = context.getBeansOfType(type);

3
src/test/java/org/springframework/data/jdbc/core/JdbcEntityTemplateIntegrationTests.java

@ -30,6 +30,7 @@ import org.springframework.context.annotation.Bean; @@ -30,6 +30,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@ -253,7 +254,7 @@ public class JdbcEntityTemplateIntegrationTests { @@ -253,7 +254,7 @@ public class JdbcEntityTemplateIntegrationTests {
JdbcEntityOperations operations(ApplicationEventPublisher publisher,
NamedParameterJdbcOperations namedParameterJdbcOperations) {
return new JdbcEntityTemplate(publisher, namedParameterJdbcOperations, new JdbcMappingContext());
return new JdbcEntityTemplate(publisher, namedParameterJdbcOperations, new JdbcMappingContext(new DefaultNamingStrategy()));
}
}
}

214
src/test/java/org/springframework/data/jdbc/core/SqlGeneratorContextBasedNamingStrategyUnitTests.java

@ -0,0 +1,214 @@ @@ -0,0 +1,214 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.mapping.PropertyPath;
/**
* Unit tests to verify a contextual {@link NamingStrategy} implementation that customizes using a user-centric {@link ThreadLocal}.
*
* NOTE: Due to the need to verify SQL generation and {@link SqlGenerator}'s package-private status suggests
* this unit test exist in this package, not {@literal org.springframework.data.jdbc.mappings.model}.
*
* @author Greg Turnquist
*/
public class SqlGeneratorContextBasedNamingStrategyUnitTests {
private final ThreadLocal<String> userHandler = new ThreadLocal<>();
/**
* Use a {@link DefaultNamingStrategy}, but override the schema with a {@link ThreadLocal}-based setting.
*/
private final NamingStrategy contextualNamingStrategy = new DefaultNamingStrategy() {
@Override
public String getSchema() {
return userHandler.get();
}
};
@Test // DATAJDBC-107
public void findOne() {
testAgainstMultipleUsers(user -> {
SqlGenerator sqlGenerator = configureSqlGenerator(contextualNamingStrategy);
String sql = sqlGenerator.getFindOne();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql) //
.startsWith("SELECT") //
.contains(user + ".DummyEntity.id AS id,") //
.contains(user + ".DummyEntity.name AS name,") //
.contains("ref.l1id AS ref_l1id") //
.contains("ref.content AS ref_content") //
.contains("FROM " + user + ".DummyEntity");
softAssertions.assertAll();
});
}
@Test // DATAJDBC-107
public void cascadingDeleteFirstLevel() {
testAgainstMultipleUsers(user -> {
SqlGenerator sqlGenerator = configureSqlGenerator(contextualNamingStrategy);
String sql = sqlGenerator.createDeleteByPath(PropertyPath.from("ref", DummyEntity.class));
assertThat(sql).isEqualTo(
"DELETE FROM " + user + ".ReferencedEntity WHERE " + user + ".DummyEntity = :rootId");
});
}
@Test // DATAJDBC-107
public void cascadingDeleteAllSecondLevel() {
testAgainstMultipleUsers(user -> {
SqlGenerator sqlGenerator = configureSqlGenerator(contextualNamingStrategy);
String sql = sqlGenerator.createDeleteByPath(PropertyPath.from("ref.further", DummyEntity.class));
assertThat(sql).isEqualTo(
"DELETE FROM " + user + ".SecondLevelReferencedEntity " +
"WHERE " + user + ".ReferencedEntity IN " +
"(SELECT l1id FROM " + user + ".ReferencedEntity " +
"WHERE " + user + ".DummyEntity = :rootId)");
});
}
@Test // DATAJDBC-107
public void deleteAll() {
testAgainstMultipleUsers(user -> {
SqlGenerator sqlGenerator = configureSqlGenerator(contextualNamingStrategy);
String sql = sqlGenerator.createDeleteAllSql(null);
assertThat(sql).isEqualTo("DELETE FROM " + user + ".DummyEntity");
});
}
@Test // DATAJDBC-107
public void cascadingDeleteAllFirstLevel() {
testAgainstMultipleUsers(user -> {
SqlGenerator sqlGenerator = configureSqlGenerator(contextualNamingStrategy);
String sql = sqlGenerator.createDeleteAllSql(PropertyPath.from("ref", DummyEntity.class));
assertThat(sql).isEqualTo(
"DELETE FROM " + user + ".ReferencedEntity WHERE " + user + ".DummyEntity IS NOT NULL");
});
}
@Test // DATAJDBC-107
public void cascadingDeleteSecondLevel() {
testAgainstMultipleUsers(user -> {
SqlGenerator sqlGenerator = configureSqlGenerator(contextualNamingStrategy);
String sql = sqlGenerator.createDeleteAllSql(PropertyPath.from("ref.further", DummyEntity.class));
assertThat(sql).isEqualTo(
"DELETE FROM " + user + ".SecondLevelReferencedEntity " +
"WHERE " + user + ".ReferencedEntity IN " +
"(SELECT l1id FROM " + user + ".ReferencedEntity " +
"WHERE " + user + ".DummyEntity IS NOT NULL)");
});
}
/**
* Take a set of user-based assertions and run them against multiple users, in different threads.
*/
private void testAgainstMultipleUsers(Consumer<String> testAssertions) {
CountDownLatch latch = new CountDownLatch(2);
threadedTest("User1", latch, testAssertions);
threadedTest("User2", latch, testAssertions);
try {
latch.await(10L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* Inside a {@link Runnable}, fetch the {@link ThreadLocal}-based username and execute the provided
* set of assertions. Then signal through the provided {@link CountDownLatch}.
*/
private void threadedTest(String user, CountDownLatch latch, Consumer<String> testAssertions) {
new Thread(() -> {
userHandler.set(user);
testAssertions.accept(user);
latch.countDown();
}).start();
}
/**
* Plug in a custom {@link NamingStrategy} for this test case.
*/
private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) {
JdbcMappingContext context = new JdbcMappingContext(namingStrategy);
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
return new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));
}
static class DummyEntity {
@Id Long id;
String name;
ReferencedEntity ref;
}
static class ReferencedEntity {
@Id Long l1id;
String content;
SecondLevelReferencedEntity further;
}
static class SecondLevelReferencedEntity {
@Id Long l2id;
String something;
}
}

201
src/test/java/org/springframework/data/jdbc/core/SqlGeneratorFixedNamingStrategyUnitTests.java

@ -0,0 +1,201 @@ @@ -0,0 +1,201 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.mapping.PropertyPath;
/**
* Unit tests to a fixed {@link NamingStrategy} implementation containing a hard wired schema, table, and property prefix.
*
* NOTE: Due to the need to verify SQL generation and {@link SqlGenerator}'s package-private status suggests
* this unit test exist in this package, not {@literal org.springframework.data.jdbc.mappings.model}.
*
* @author Greg Turnquist
*/
public class SqlGeneratorFixedNamingStrategyUnitTests {
final NamingStrategy fixedCustomTablePrefixStrategy = new DefaultNamingStrategy() {
@Override
public String getSchema() {
return "FixedCustomSchema";
}
@Override
public String getTableName(Class<?> type) {
return "FixedCustomTablePrefix_" + type.getSimpleName();
}
@Override
public String getColumnName(JdbcPersistentProperty property) {
return "FixedCustomPropertyPrefix_" + property.getName();
}
};
final NamingStrategy upperCaseLowerCaseStrategy = new DefaultNamingStrategy() {
@Override
public String getTableName(Class<?> type) {
return type.getSimpleName().toUpperCase();
}
@Override
public String getColumnName(JdbcPersistentProperty property) {
return property.getName().toLowerCase();
}
};
@Test // DATAJDBC-107
public void findOneWithOverriddenFixedTableName() {
SqlGenerator sqlGenerator = configureSqlGenerator(fixedCustomTablePrefixStrategy);
String sql = sqlGenerator.getFindOne();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql) //
.startsWith("SELECT") //
.contains("FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_id AS FixedCustomPropertyPrefix_id,") //
.contains("FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_name AS FixedCustomPropertyPrefix_name,") //
.contains("ref.FixedCustomPropertyPrefix_l1id AS ref_FixedCustomPropertyPrefix_l1id") //
.contains("ref.FixedCustomPropertyPrefix_content AS ref_FixedCustomPropertyPrefix_content") //
.contains("FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity");
softAssertions.assertAll();
}
@Test // DATAJDBC-107
public void findOneWithUppercasedTablesAndLowercasedColumns() {
SqlGenerator sqlGenerator = configureSqlGenerator(upperCaseLowerCaseStrategy);
String sql = sqlGenerator.getFindOne();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql) //
.startsWith("SELECT") //
.contains("DUMMYENTITY.id AS id,") //
.contains("DUMMYENTITY.name AS name,") //
.contains("ref.l1id AS ref_l1id") //
.contains("ref.content AS ref_content") //
.contains("FROM DUMMYENTITY");
softAssertions.assertAll();
}
@Test // DATAJDBC-107
public void cascadingDeleteFirstLevel() {
SqlGenerator sqlGenerator = configureSqlGenerator(fixedCustomTablePrefixStrategy);
String sql = sqlGenerator.createDeleteByPath(PropertyPath.from("ref", DummyEntity.class));
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " +
"WHERE FixedCustomSchema.FixedCustomTablePrefix_DummyEntity = :rootId");
}
@Test // DATAJDBC-107
public void cascadingDeleteAllSecondLevel() {
SqlGenerator sqlGenerator = configureSqlGenerator(fixedCustomTablePrefixStrategy);
String sql = sqlGenerator.createDeleteByPath(PropertyPath.from("ref.further", DummyEntity.class));
assertThat(sql).isEqualTo(
"DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity " +
"WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity IN " +
"(SELECT FixedCustomPropertyPrefix_l1id " +
"FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " +
"WHERE FixedCustomSchema.FixedCustomTablePrefix_DummyEntity = :rootId)");
}
@Test // DATAJDBC-107
public void deleteAll() {
SqlGenerator sqlGenerator = configureSqlGenerator(fixedCustomTablePrefixStrategy);
String sql = sqlGenerator.createDeleteAllSql(null);
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity");
}
@Test // DATAJDBC-107
public void cascadingDeleteAllFirstLevel() {
SqlGenerator sqlGenerator = configureSqlGenerator(fixedCustomTablePrefixStrategy);
String sql = sqlGenerator.createDeleteAllSql(PropertyPath.from("ref", DummyEntity.class));
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " +
"WHERE FixedCustomSchema.FixedCustomTablePrefix_DummyEntity IS NOT NULL");
}
@Test // DATAJDBC-107
public void cascadingDeleteSecondLevel() {
SqlGenerator sqlGenerator = configureSqlGenerator(fixedCustomTablePrefixStrategy);
String sql = sqlGenerator.createDeleteAllSql(PropertyPath.from("ref.further", DummyEntity.class));
assertThat(sql).isEqualTo(
"DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity " +
"WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity IN " +
"(SELECT FixedCustomPropertyPrefix_l1id " +
"FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " +
"WHERE FixedCustomSchema.FixedCustomTablePrefix_DummyEntity IS NOT NULL)");
}
/**
* Plug in a custom {@link NamingStrategy} for this test case.
*
* @param namingStrategy
*/
private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) {
JdbcMappingContext context = new JdbcMappingContext(namingStrategy);
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
return new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));
}
static class DummyEntity {
@Id Long id;
String name;
ReferencedEntity ref;
}
static class ReferencedEntity {
@Id Long l1id;
String content;
SecondLevelReferencedEntity further;
}
static class SecondLevelReferencedEntity {
@Id Long l2id;
String something;
}
}

30
src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java

@ -18,10 +18,13 @@ package org.springframework.data.jdbc.core; @@ -18,10 +18,13 @@ package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.mapping.PropertyPath;
/**
@ -31,22 +34,33 @@ import org.springframework.data.mapping.PropertyPath; @@ -31,22 +34,33 @@ import org.springframework.data.mapping.PropertyPath;
*/
public class SqlGeneratorUnitTests {
JdbcMappingContext context = new JdbcMappingContext();
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
SqlGenerator sqlGenerator = new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));
private NamingStrategy namingStrategy;
private JdbcMappingContext context;
private JdbcPersistentEntity<?> persistentEntity;
private SqlGenerator sqlGenerator;
@Before
public void setUp() {
this.namingStrategy = new DefaultNamingStrategy();
this.context = new JdbcMappingContext(namingStrategy);
this.persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
this.sqlGenerator = new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));
}
@Test // DATAJDBC-112
public void findOne() {
String sql = sqlGenerator.getFindOne();
new SoftAssertions().assertThat(sql) //
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql) //
.startsWith("SELECT") //
.contains("DummyEntity.id as id,") //
.contains("DummyEntity.name as name,") //
.contains("ref.id AS ref_id") //
.contains("DummyEntity.id AS id,") //
.contains("DummyEntity.name AS name,") //
.contains("ref.l1id AS ref_l1id") //
.contains("ref.content AS ref_content").contains(" FROM DummyEntity");
new SoftAssertions().assertAll();
softAssertions.assertAll();
}
@Test // DATAJDBC-112

3
src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityDeleteWriterUnitTests.java

@ -25,6 +25,7 @@ import org.mockito.junit.MockitoJUnitRunner; @@ -25,6 +25,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.conversion.AggregateChange.Kind;
import org.springframework.data.jdbc.core.conversion.DbAction.Delete;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
/**
@ -35,7 +36,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; @@ -35,7 +36,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@RunWith(MockitoJUnitRunner.class)
public class JdbcEntityDeleteWriterUnitTests {
JdbcEntityDeleteWriter converter = new JdbcEntityDeleteWriter(new JdbcMappingContext());
JdbcEntityDeleteWriter converter = new JdbcEntityDeleteWriter(new JdbcMappingContext(new DefaultNamingStrategy()));
@Test
public void deleteDeletesTheEntityAndReferencedEntities() {

3
src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterUnitTests.java

@ -27,6 +27,7 @@ import org.springframework.data.jdbc.core.conversion.AggregateChange.Kind; @@ -27,6 +27,7 @@ import org.springframework.data.jdbc.core.conversion.AggregateChange.Kind;
import org.springframework.data.jdbc.core.conversion.DbAction.Delete;
import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
import org.springframework.data.jdbc.core.conversion.DbAction.Update;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
/**
@ -37,7 +38,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; @@ -37,7 +38,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@RunWith(MockitoJUnitRunner.class)
public class JdbcEntityWriterUnitTests {
JdbcEntityWriter converter = new JdbcEntityWriter(new JdbcMappingContext());
JdbcEntityWriter converter = new JdbcEntityWriter(new JdbcMappingContext(new DefaultNamingStrategy()));
@Test // DATAJDBC-112
public void newEntityGetsConvertedToOneInsert() {

6
src/test/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentPropertyUnitTests.java

@ -28,7 +28,7 @@ import org.junit.Test; @@ -28,7 +28,7 @@ import org.junit.Test;
import org.springframework.data.mapping.PropertyHandler;
/**
* Unti tests for the {@link BasicJdbcPersistentProperty}.
* Unit tests for the {@link BasicJdbcPersistentProperty}.
*
* @author Jens Schauder
*/
@ -36,7 +36,9 @@ public class BasicJdbcPersistentPropertyUnitTests { @@ -36,7 +36,9 @@ public class BasicJdbcPersistentPropertyUnitTests {
@Test // DATAJDBC-104
public void enumGetsStoredAsString() {
JdbcPersistentEntity<?> persistentEntity = new JdbcMappingContext().getRequiredPersistentEntity(DummyEntity.class);
JdbcPersistentEntity<?> persistentEntity = new JdbcMappingContext(new DefaultNamingStrategy())
.getRequiredPersistentEntity(DummyEntity.class);
persistentEntity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) p -> {
switch (p.getName()) {

22
src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java

@ -33,6 +33,8 @@ import org.springframework.context.annotation.ComponentScan; @@ -33,6 +33,8 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.repository.CrudRepository;
@ -128,22 +130,36 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @@ -128,22 +130,36 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
return JdbcRepositoryIdGenerationIntegrationTests.class;
}
/**
* {@link NamingStrategy} that harmlessly uppercases the table name,
* demonstrating how to inject one while not breaking existing SQL operations.
*/
@Bean
NamingStrategy namingStrategy() {
return new DefaultNamingStrategy() {
@Override
public String getTableName(Class<?> type) {
return type.getSimpleName().toUpperCase();
}
};
}
@Bean
NamedParameterJdbcTemplate template(DataSource db) {
return new NamedParameterJdbcTemplate(db);
}
@Bean
ReadOnlyIdEntityRepository readOnlyIdRepository(DataSource db) {
ReadOnlyIdEntityRepository readOnlyIdRepository(DataSource db, NamingStrategy namingStrategy) {
return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class))
return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class), namingStrategy)
.getRepository(ReadOnlyIdEntityRepository.class);
}
@Bean
PrimitiveIdEntityRepository primitiveIdRepository(NamedParameterJdbcTemplate template) {
return new JdbcRepositoryFactory(template, mock(ApplicationEventPublisher.class))
return new JdbcRepositoryFactory(template, mock(ApplicationEventPublisher.class), new DefaultNamingStrategy())
.getRepository(PrimitiveIdEntityRepository.class);
}
}

3
src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java

@ -24,6 +24,7 @@ import org.springframework.data.jdbc.mapping.event.BeforeInsert; @@ -24,6 +24,7 @@ import org.springframework.data.jdbc.mapping.event.BeforeInsert;
import org.springframework.data.jdbc.mapping.event.BeforeUpdate;
import org.springframework.data.jdbc.mapping.event.Identifier;
import org.springframework.data.jdbc.mapping.event.JdbcEvent;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@ -43,7 +44,7 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -43,7 +44,7 @@ public class SimpleJdbcRepositoryEventsUnitTests {
public void before() {
NamedParameterJdbcOperations operations = createIdGeneratingOperations();
JdbcRepositoryFactory factory = new JdbcRepositoryFactory(operations, publisher);
JdbcRepositoryFactory factory = new JdbcRepositoryFactory(operations, publisher, new DefaultNamingStrategy());
repository = factory.getRepository(DummyEntityRepository.class);
}

3
src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

@ -22,6 +22,7 @@ import org.springframework.context.ApplicationEventPublisher; @@ -22,6 +22,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@ -41,7 +42,7 @@ public class TestConfiguration { @@ -41,7 +42,7 @@ public class TestConfiguration {
@Bean
JdbcRepositoryFactory jdbcRepositoryFactory() {
return new JdbcRepositoryFactory(namedParameterJdbcTemplate(), publisher);
return new JdbcRepositoryFactory(namedParameterJdbcTemplate(), publisher, new DefaultNamingStrategy());
}
@Bean

Loading…
Cancel
Save