The back reference generation is now configurable.
The default version is the behavior that existed so far: The back reference is the table name as generated by the `NamingStrategy` without taking `@Table` annotations into account. The new alternative is to take `@Table` into account. The behavior can be configured by setting the `foreignKeyNaming` property on the `RelationalMappingContext`. Closes #1161 Closes #1147 Original pull request: #1324.
This commit is contained in:
committed by
Mark Paluch
parent
15796b88fe
commit
40446f9ca9
@@ -298,10 +298,10 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
Assert.notNull(propertyPath, "propertyPath must not be null");
|
||||
|
||||
PersistentPropertyPathExtension path = new PersistentPropertyPathExtension(context, propertyPath);
|
||||
|
||||
Class<?> actualType = path.getActualType();
|
||||
|
||||
String findAllByProperty = sql(actualType) //
|
||||
.getFindAllByProperty(identifier, path.getQualifierColumn(), path.isOrdered());
|
||||
.getFindAllByProperty(identifier, propertyPath);
|
||||
|
||||
RowMapper<?> rowMapper = path.isMap() ? this.getMapEntityRowMapper(path, identifier)
|
||||
: this.getEntityRowMapper(path, identifier);
|
||||
|
||||
@@ -37,7 +37,7 @@ class SqlContext {
|
||||
SqlContext(RelationalPersistentEntity<?> entity) {
|
||||
|
||||
this.entity = entity;
|
||||
this.table = Table.create(entity.getTableName());
|
||||
this.table = Table.create(entity.getFullTableName());
|
||||
}
|
||||
|
||||
Column getIdColumn() {
|
||||
|
||||
@@ -200,6 +200,26 @@ class SqlGenerator {
|
||||
return render(selectBuilder(Collections.emptyList(), pageable.getSort(), pageable).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query for selecting all simple properties of an entity, including those for one-to-one relationships.
|
||||
* Results are limited to those rows referencing some parent entity. This is used to select values for a complex
|
||||
* property ({@link Set}, {@link Map} ...) based on a referencing entity.
|
||||
*
|
||||
* @param parentIdentifier name of the column of the FK back to the referencing entity.
|
||||
* @param propertyPath used to determine if the property is ordered and if there is a key column.
|
||||
* @return a SQL String.
|
||||
*/
|
||||
String getFindAllByProperty(Identifier parentIdentifier,
|
||||
PersistentPropertyPath<? extends RelationalPersistentProperty> propertyPath) {
|
||||
|
||||
Assert.notNull(parentIdentifier, "identifier must not be null");
|
||||
Assert.notNull(propertyPath, "propertyPath must not be null");
|
||||
|
||||
PersistentPropertyPathExtension path = new PersistentPropertyPathExtension(mappingContext, propertyPath);
|
||||
|
||||
return getFindAllByProperty(parentIdentifier, path.getQualifierColumn(), path.isOrdered());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query for selecting all simple properties of an entity, including those for one-to-one relationships.
|
||||
* Results are limited to those rows referencing some other entity using the column specified by
|
||||
@@ -915,7 +935,7 @@ class SqlGenerator {
|
||||
private SelectBuilder.SelectOrdered applyQueryOnSelect(Query query, MapSqlParameterSource parameterSource,
|
||||
SelectBuilder.SelectWhere selectBuilder) {
|
||||
|
||||
Table table = Table.create(this.entity.getTableName());
|
||||
Table table = Table.create(this.entity.getFullTableName());
|
||||
|
||||
SelectBuilder.SelectOrdered selectOrdered = query //
|
||||
.getCriteria() //
|
||||
|
||||
@@ -38,7 +38,7 @@ class SqlContext {
|
||||
SqlContext(RelationalPersistentEntity<?> entity) {
|
||||
|
||||
this.entity = entity;
|
||||
this.table = Table.create(entity.getTableName());
|
||||
this.table = Table.create(entity.getFullTableName());
|
||||
}
|
||||
|
||||
Column getIdColumn() {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.jdbc.core.convert;
|
||||
import static java.util.Collections.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.SoftAssertions.*;
|
||||
import static org.springframework.data.relational.core.mapping.ForeignKeyNaming.*;
|
||||
import static org.springframework.data.relational.core.sql.SqlIdentifier.*;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -41,6 +42,7 @@ import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.dialect.PostgresDialect;
|
||||
import org.springframework.data.relational.core.dialect.SqlServerDialect;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.DefaultNamingStrategy;
|
||||
import org.springframework.data.relational.core.mapping.NamingStrategy;
|
||||
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
@@ -763,7 +765,7 @@ class SqlGeneratorUnitTests {
|
||||
@Test // GH-1192
|
||||
void selectByQueryValidTest() {
|
||||
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class);
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class);
|
||||
|
||||
DummyEntity probe = new DummyEntity();
|
||||
probe.name = "Diego";
|
||||
@@ -862,6 +864,50 @@ class SqlGeneratorUnitTests {
|
||||
.containsOnly(entry("x_name", probe.name));
|
||||
}
|
||||
|
||||
@Test // GH-1161
|
||||
void backReferenceShouldConsiderRenamedParent() {
|
||||
|
||||
context.setForeignKeyNaming(APPLY_RENAMING);
|
||||
|
||||
String sql = sqlGenerator.createDeleteInByPath(getPath("ref", RenamedDummy.class));
|
||||
|
||||
assertThat(sql).isEqualTo("DELETE FROM referenced_entity WHERE referenced_entity.renamed IN (:ids)");
|
||||
|
||||
}
|
||||
|
||||
@Test // GH-1161
|
||||
void backReferenceShouldIgnoreRenamedParent() {
|
||||
|
||||
context.setForeignKeyNaming(IGNORE_RENAMING);
|
||||
|
||||
String sql = sqlGenerator.createDeleteInByPath(getPath("ref", RenamedDummy.class));
|
||||
|
||||
assertThat(sql).isEqualTo("DELETE FROM referenced_entity WHERE referenced_entity.renamed_dummy IN (:ids)");
|
||||
}
|
||||
|
||||
@Test // GH-1161
|
||||
void keyColumnShouldConsiderRenamedParent() {
|
||||
|
||||
context.setForeignKeyNaming(APPLY_RENAMING);
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(ReferencedEntity.class);
|
||||
String sql = sqlGenerator.getFindAllByProperty(Identifier.of(unquoted("parentId"), 23, RenamedDummy.class), getPath("ref", RenamedDummy.class));
|
||||
|
||||
assertThat(sql)
|
||||
.contains("referenced_entity.renamed_key AS renamed_key", "WHERE referenced_entity.parentId");
|
||||
}
|
||||
|
||||
@Test // GH-1161
|
||||
void keyColumnShouldIgnoreRenamedParent() {
|
||||
|
||||
context.setForeignKeyNaming(IGNORE_RENAMING);
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(ReferencedEntity.class);
|
||||
String sql = sqlGenerator.getFindAllByProperty(Identifier.of(unquoted("parentId"), 23, RenamedDummy.class), getPath("ref", RenamedDummy.class));
|
||||
|
||||
assertThat(sql)
|
||||
.contains("referenced_entity.renamed_dummy_key AS renamed_dummy_key", "WHERE referenced_entity.parentId");
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private SqlIdentifier getAlias(Object maybeAliased) {
|
||||
|
||||
@@ -885,8 +931,7 @@ class SqlGeneratorUnitTests {
|
||||
@SuppressWarnings("unused")
|
||||
static class DummyEntity {
|
||||
|
||||
@Column("id1")
|
||||
@Id Long id;
|
||||
@Column("id1") @Id Long id;
|
||||
String name;
|
||||
ReferencedEntity ref;
|
||||
Set<Element> elements;
|
||||
@@ -895,6 +940,15 @@ class SqlGeneratorUnitTests {
|
||||
Map<Long, ReferencedEntity> mappedReference;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@org.springframework.data.relational.core.mapping.Table("renamed")
|
||||
static class RenamedDummy {
|
||||
|
||||
@Id Long id;
|
||||
String name;
|
||||
Map<String, ReferencedEntity> ref;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class VersionedEntity extends DummyEntity {
|
||||
@Version Integer version;
|
||||
@@ -936,11 +990,11 @@ class SqlGeneratorUnitTests {
|
||||
String name;
|
||||
}
|
||||
|
||||
private static class PrefixingNamingStrategy implements NamingStrategy {
|
||||
private static class PrefixingNamingStrategy extends DefaultNamingStrategy {
|
||||
|
||||
@Override
|
||||
public String getColumnName(RelationalPersistentProperty property) {
|
||||
return "x_" + NamingStrategy.super.getColumnName(property);
|
||||
return "x_" + super.getColumnName(property);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -964,8 +1018,7 @@ class SqlGeneratorUnitTests {
|
||||
|
||||
// these column names behave like single double quote in the name since the get quoted and then doubling the double
|
||||
// quote escapes it.
|
||||
@Id
|
||||
@Column("test\"\"_@id") Long id;
|
||||
@Id @Column("test\"\"_@id") Long id;
|
||||
@Column("test\"\"_@123") String name;
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
||||
|
||||
@Override
|
||||
public SqlIdentifier getTableName(Class<?> type) {
|
||||
return getRequiredPersistentEntity(type).getTableName();
|
||||
return getRequiredPersistentEntity(type).getFullTableName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -465,7 +465,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
return doInsert(entity, getRequiredEntity(entity).getTableName());
|
||||
return doInsert(entity, getRequiredEntity(entity).getFullTableName());
|
||||
}
|
||||
|
||||
<T> Mono<T> doInsert(T entity, SqlIdentifier tableName) {
|
||||
@@ -564,7 +564,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
return doUpdate(entity, getRequiredEntity(entity).getTableName());
|
||||
return doUpdate(entity, getRequiredEntity(entity).getFullTableName());
|
||||
}
|
||||
|
||||
private <T> Mono<T> doUpdate(T entity, SqlIdentifier tableName) {
|
||||
@@ -644,13 +644,13 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
private <T> String formatOptimisticLockingExceptionMessage(T entity, RelationalPersistentEntity<T> persistentEntity) {
|
||||
|
||||
return String.format("Failed to update table [%s]; Version does not match for row with Id [%s]",
|
||||
persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
|
||||
persistentEntity.getFullTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
|
||||
}
|
||||
|
||||
private <T> String formatTransientEntityExceptionMessage(T entity, RelationalPersistentEntity<T> persistentEntity) {
|
||||
|
||||
return String.format("Failed to update table [%s]; Row with Id [%s] does not exist",
|
||||
persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
|
||||
persistentEntity.getFullTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -744,14 +744,14 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
}
|
||||
|
||||
SqlIdentifier getTableName(Class<?> entityClass) {
|
||||
return getRequiredEntity(entityClass).getTableName();
|
||||
return getRequiredEntity(entityClass).getFullTableName();
|
||||
}
|
||||
|
||||
SqlIdentifier getTableNameOrEmpty(Class<?> entityClass) {
|
||||
|
||||
RelationalPersistentEntity<?> entity = this.mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
return entity != null ? entity.getTableName() : SqlIdentifier.EMPTY;
|
||||
return entity != null ? entity.getFullTableName() : SqlIdentifier.EMPTY;
|
||||
}
|
||||
|
||||
private RelationalPersistentEntity<?> getRequiredEntity(Class<?> entityClass) {
|
||||
|
||||
@@ -82,4 +82,9 @@ class CachingNamingStrategy implements NamingStrategy {
|
||||
public String getColumnName(RelationalPersistentProperty property) {
|
||||
return columnNames.computeIfAbsent(property, delegate::getColumnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForeignKeyNaming(ForeignKeyNaming foreignKeyNaming) {
|
||||
delegate.setForeignKeyNaming(foreignKeyNaming);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.relational.core.mapping;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The default naming strategy used by Spring Data Relational. Names are in SNAKE_CASE.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.4
|
||||
*/
|
||||
public class DefaultNamingStrategy implements NamingStrategy {
|
||||
|
||||
/**
|
||||
* Since in most cases it doesn't make sense to have more than one {@link NamingStrategy} use of this instance is
|
||||
* recommended.
|
||||
*/
|
||||
public static NamingStrategy INSTANCE = new DefaultNamingStrategy();
|
||||
|
||||
private ForeignKeyNaming foreignKeyNaming = ForeignKeyNaming.IGNORE_RENAMING;
|
||||
|
||||
@Override
|
||||
public void setForeignKeyNaming(ForeignKeyNaming foreignKeyNaming) {
|
||||
|
||||
Assert.notNull(foreignKeyNaming, "foreignKeyNaming must not be null");
|
||||
|
||||
this.foreignKeyNaming = foreignKeyNaming;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReverseColumnName(RelationalPersistentProperty property) {
|
||||
|
||||
return getColumnNameReferencing(property.getOwner());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReverseColumnName(PersistentPropertyPathExtension path) {
|
||||
|
||||
RelationalPersistentEntity<?> leafEntity = path.getIdDefiningParentPath().getLeafEntity();
|
||||
|
||||
return getColumnNameReferencing(leafEntity);
|
||||
}
|
||||
|
||||
private String getColumnNameReferencing(RelationalPersistentEntity<?> leafEntity) {
|
||||
|
||||
Assert.state(leafEntity != null, "Leaf Entity must not be null.");
|
||||
|
||||
if (foreignKeyNaming == ForeignKeyNaming.IGNORE_RENAMING) {
|
||||
return getTableName(leafEntity.getType());
|
||||
}
|
||||
|
||||
return leafEntity.getSimpleTableName().getReference();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.relational.core.mapping;
|
||||
|
||||
/**
|
||||
* Enum for determining how the names of back references should get generated.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.4
|
||||
*/
|
||||
public enum ForeignKeyNaming {
|
||||
/**
|
||||
* This strategy takes names specified via {@link Table} annotation into account.
|
||||
*/
|
||||
APPLY_RENAMING,
|
||||
/**
|
||||
* This strategy does not take names specified via {@link Table} annotation into account.
|
||||
*/
|
||||
IGNORE_RENAMING
|
||||
}
|
||||
@@ -38,8 +38,11 @@ public interface NamingStrategy {
|
||||
* Empty implementation of the interface utilizing only the default implementation.
|
||||
* <p>
|
||||
* Using this avoids creating essentially the same class over and over again.
|
||||
*
|
||||
* @deprecated use {@link DefaultNamingStrategy#INSTANCE} instead.
|
||||
*/
|
||||
NamingStrategy INSTANCE = new NamingStrategy() {};
|
||||
@Deprecated(since = "2.4")
|
||||
NamingStrategy INSTANCE = DefaultNamingStrategy.INSTANCE;
|
||||
|
||||
/**
|
||||
* Defaults to no schema.
|
||||
@@ -82,7 +85,7 @@ public interface NamingStrategy {
|
||||
|
||||
Assert.notNull(property, "Property must not be null");
|
||||
|
||||
return property.getOwner().getTableName().getReference(IdentifierProcessing.NONE);
|
||||
return property.getOwner().getSimpleTableName().getReference(IdentifierProcessing.NONE);
|
||||
}
|
||||
|
||||
default String getReverseColumnName(PersistentPropertyPathExtension path) {
|
||||
@@ -102,4 +105,12 @@ public interface NamingStrategy {
|
||||
|
||||
return getReverseColumnName(property) + "_key";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ForeignKeyNaming} strategy used in this {@link NamingStrategy}.
|
||||
*
|
||||
* @param foreignKeyNaming the ForeignKeyNaming strategy to be used. Must not be {@literal null}.
|
||||
* @since 2.4
|
||||
*/
|
||||
default void setForeignKeyNaming(ForeignKeyNaming foreignKeyNaming) {}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public class RelationalMappingContext
|
||||
|
||||
private final NamingStrategy namingStrategy;
|
||||
private boolean forceQuote = true;
|
||||
private ForeignKeyNaming foreignKeyNaming = ForeignKeyNaming.IGNORE_RENAMING;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RelationalMappingContext}.
|
||||
@@ -53,6 +54,7 @@ public class RelationalMappingContext
|
||||
|
||||
Assert.notNull(namingStrategy, "NamingStrategy must not be null");
|
||||
|
||||
namingStrategy.setForeignKeyNaming(foreignKeyNaming);
|
||||
this.namingStrategy = new CachingNamingStrategy(namingStrategy);
|
||||
|
||||
setSimpleTypeHolder(SimpleTypeHolder.DEFAULT);
|
||||
@@ -101,4 +103,20 @@ public class RelationalMappingContext
|
||||
public NamingStrategy getNamingStrategy() {
|
||||
return this.namingStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ForeignKeyNaming} to be used by this mapping context.
|
||||
*
|
||||
* @param foreignKeyNaming must not be {@literal null}.
|
||||
* @since 2.4
|
||||
*/
|
||||
public void setForeignKeyNaming(ForeignKeyNaming foreignKeyNaming) {
|
||||
|
||||
Assert.notNull(foreignKeyNaming, "foreignKeyNaming must not be null");
|
||||
|
||||
this.foreignKeyNaming = foreignKeyNaming;
|
||||
if (namingStrategy != null) {
|
||||
namingStrategy.setForeignKeyNaming(foreignKeyNaming);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,34 @@ public interface RelationalPersistentEntity<T> extends MutablePersistentEntity<T
|
||||
* Returns the name of the table backing the given entity.
|
||||
*
|
||||
* @return the table name.
|
||||
* @deprecated Use either {@link #getFullTableName()} or {@link #getSimpleTableName()}
|
||||
*/
|
||||
@Deprecated(since = "2.4")
|
||||
SqlIdentifier getTableName();
|
||||
|
||||
/**
|
||||
* Returns the name of the table backing the given entity, including the schema.
|
||||
*
|
||||
* @return the table name including the schema if there is any specified.
|
||||
* @since 2.4
|
||||
*/
|
||||
default SqlIdentifier getFullTableName() {
|
||||
return getTableName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the table backing the given entity, without any schema.
|
||||
*
|
||||
* @return the table name.
|
||||
* @since 2.4
|
||||
*/
|
||||
SqlIdentifier getSimpleTableName();
|
||||
|
||||
/**
|
||||
* Returns the column representing the identifier.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
SqlIdentifier getIdColumn();
|
||||
|
||||
}
|
||||
|
||||
@@ -81,11 +81,16 @@ class RelationalPersistentEntityImpl<T> extends BasicPersistentEntity<T, Relatio
|
||||
|
||||
@Override
|
||||
public SqlIdentifier getTableName() {
|
||||
return getFullTableName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlIdentifier getFullTableName() {
|
||||
|
||||
SqlIdentifier schema = determineCurrentEntitySchema();
|
||||
Optional<SqlIdentifier> explicitlySpecifiedTableName = tableName.get();
|
||||
|
||||
final SqlIdentifier schemalessTableIdentifier = createDerivedSqlIdentifier(namingStrategy.getTableName(getType()));
|
||||
SqlIdentifier schemalessTableIdentifier = createDerivedSqlIdentifier(namingStrategy.getTableName(getType()));
|
||||
|
||||
if (schema == null) {
|
||||
return explicitlySpecifiedTableName.orElse(schemalessTableIdentifier);
|
||||
@@ -96,6 +101,15 @@ class RelationalPersistentEntityImpl<T> extends BasicPersistentEntity<T, Relatio
|
||||
.orElse(SqlIdentifier.from(schema, schemalessTableIdentifier));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlIdentifier getSimpleTableName() {
|
||||
|
||||
Optional<SqlIdentifier> explicitlySpecifiedTableName = tableName.get();
|
||||
SqlIdentifier schemalessTableIdentifier = createDerivedSqlIdentifier(namingStrategy.getTableName(getType()));
|
||||
|
||||
return explicitlySpecifiedTableName.orElse(schemalessTableIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link SqlIdentifier} representing the current entity schema. If the schema is not specified, neither
|
||||
* explicitly, nor via {@link NamingStrategy}, then return {@link null}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SimpleRelationalEntityMetadata<T> implements RelationalEntityMetada
|
||||
}
|
||||
|
||||
public SqlIdentifier getTableName() {
|
||||
return tableEntity.getTableName();
|
||||
return tableEntity.getFullTableName();
|
||||
}
|
||||
|
||||
public RelationalPersistentEntity<?> getTableEntity() {
|
||||
|
||||
@@ -87,7 +87,7 @@ public class MappingRelationalEntityInformation<T, ID> extends PersistentEntityI
|
||||
}
|
||||
|
||||
public SqlIdentifier getTableName() {
|
||||
return customTableName == null ? entityMetadata.getTableName() : customTableName;
|
||||
return customTableName == null ? entityMetadata.getFullTableName() : customTableName;
|
||||
}
|
||||
|
||||
public String getIdAttribute() {
|
||||
|
||||
@@ -42,6 +42,8 @@ public class RelationalPersistentEntityImplUnitTests {
|
||||
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(DummySubEntity.class);
|
||||
|
||||
assertThat(entity.getTableName()).isEqualTo(quoted("dummy_sub_entity"));
|
||||
assertThat(entity.getFullTableName()).isEqualTo(quoted("dummy_sub_entity"));
|
||||
assertThat(entity.getSimpleTableName()).isEqualTo(quoted("dummy_sub_entity"));
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-294
|
||||
@@ -58,6 +60,8 @@ public class RelationalPersistentEntityImplUnitTests {
|
||||
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(DummyEntityWithEmptyAnnotation.class);
|
||||
|
||||
assertThat(entity.getTableName()).isEqualTo(quoted("DUMMY_ENTITY_WITH_EMPTY_ANNOTATION"));
|
||||
assertThat(entity.getFullTableName()).isEqualTo(quoted("DUMMY_ENTITY_WITH_EMPTY_ANNOTATION"));
|
||||
assertThat(entity.getSimpleTableName()).isEqualTo(quoted("DUMMY_ENTITY_WITH_EMPTY_ANNOTATION"));
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-491
|
||||
@@ -66,8 +70,16 @@ public class RelationalPersistentEntityImplUnitTests {
|
||||
mappingContext = new RelationalMappingContext(NamingStrategyWithSchema.INSTANCE);
|
||||
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(DummyEntityWithEmptyAnnotation.class);
|
||||
|
||||
SqlIdentifier simpleExpected = quoted("DUMMY_ENTITY_WITH_EMPTY_ANNOTATION");
|
||||
SqlIdentifier fullExpected = SqlIdentifier.from(quoted("MY_SCHEMA"), simpleExpected);
|
||||
|
||||
assertThat(entity.getTableName())
|
||||
.isEqualTo(SqlIdentifier.from(quoted("MY_SCHEMA"), quoted("DUMMY_ENTITY_WITH_EMPTY_ANNOTATION")));
|
||||
.isEqualTo(fullExpected);
|
||||
assertThat(entity.getFullTableName())
|
||||
.isEqualTo(fullExpected);
|
||||
assertThat(entity.getSimpleTableName())
|
||||
.isEqualTo(simpleExpected);
|
||||
|
||||
assertThat(entity.getTableName().toSql(IdentifierProcessing.ANSI))
|
||||
.isEqualTo("\"MY_SCHEMA\".\"DUMMY_ENTITY_WITH_EMPTY_ANNOTATION\"");
|
||||
}
|
||||
@@ -76,21 +88,25 @@ public class RelationalPersistentEntityImplUnitTests {
|
||||
void testRelationalPersistentEntitySchemaNameChoice() {
|
||||
|
||||
mappingContext = new RelationalMappingContext(NamingStrategyWithSchema.INSTANCE);
|
||||
RelationalPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(EntityWithSchemaAndName.class);
|
||||
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(EntityWithSchemaAndName.class);
|
||||
|
||||
SqlIdentifier tableName = persistentEntity.getTableName();
|
||||
|
||||
assertThat(tableName).isEqualTo(SqlIdentifier.from(SqlIdentifier.quoted("DART_VADER"), quoted("I_AM_THE_SENATE")));
|
||||
SqlIdentifier simpleExpected = quoted("I_AM_THE_SENATE");
|
||||
SqlIdentifier expected = SqlIdentifier.from(quoted("DART_VADER"), simpleExpected);
|
||||
assertThat(entity.getTableName()).isEqualTo(expected);
|
||||
assertThat(entity.getFullTableName()).isEqualTo(expected);
|
||||
assertThat(entity.getSimpleTableName()).isEqualTo(simpleExpected);
|
||||
}
|
||||
|
||||
@Test // GH-1099
|
||||
void specifiedSchemaGetsCombinedWithNameFromNamingStrategy() {
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(EntityWithSchema.class);
|
||||
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(EntityWithSchema.class);
|
||||
|
||||
SqlIdentifier tableName = persistentEntity.getTableName();
|
||||
|
||||
assertThat(tableName).isEqualTo(SqlIdentifier.from(quoted("ANAKYN_SKYWALKER"), quoted("ENTITY_WITH_SCHEMA")));
|
||||
SqlIdentifier simpleExpected = quoted("ENTITY_WITH_SCHEMA");
|
||||
SqlIdentifier expected = SqlIdentifier.from(quoted("ANAKYN_SKYWALKER"), simpleExpected);
|
||||
assertThat(entity.getTableName()).isEqualTo(expected);
|
||||
assertThat(entity.getFullTableName()).isEqualTo(expected);
|
||||
assertThat(entity.getSimpleTableName()).isEqualTo(simpleExpected);
|
||||
}
|
||||
|
||||
@Table(schema = "ANAKYN_SKYWALKER")
|
||||
|
||||
@@ -216,22 +216,23 @@ The properties of the following types are currently supported:
|
||||
* References to other entities.
|
||||
They are considered a one-to-one relationship, or an embedded type.
|
||||
It is optional for one-to-one relationship entities to have an `id` attribute.
|
||||
The table of the referenced entity is expected to have an additional column named the same as the table of the referencing entity.
|
||||
You can change this name by implementing `NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)`.
|
||||
The table of the referenced entity is expected to have an additional column with a name based on the referencing entity see <<jdbc.entity-persistence.types.backrefs>>.
|
||||
Embedded entities do not need an `id`.
|
||||
If one is present it gets ignored.
|
||||
|
||||
* `Set<some entity>` is considered a one-to-many relationship.
|
||||
The table of the referenced entity is expected to have an additional column named the same as the table of the referencing entity.
|
||||
You can change this name by implementing `NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)`.
|
||||
The table of the referenced entity is expected to have an additional column with a name based on the referencing entity see <<jdbc.entity-persistence.types.backrefs>>.
|
||||
|
||||
* `Map<simple type, some entity>` is considered a qualified one-to-many relationship.
|
||||
The table of the referenced entity is expected to have two additional columns: One named the same as the table of the referencing entity for the foreign key and one with the same name and an additional `_key` suffix for the map key.
|
||||
The table of the referenced entity is expected to have two additional columns: One named based on the referencing entity for the foreign key (see <<jdbc.entity-persistence.types.backrefs>>) and one with the same name and an additional `_key` suffix for the map key.
|
||||
You can change this behavior by implementing `NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)` and `NamingStrategy.getKeyColumn(RelationalPersistentProperty property)`, respectively.
|
||||
Alternatively you may annotate the attribute with `@MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name")`
|
||||
|
||||
* `List<some entity>` is mapped as a `Map<Integer, some entity>`.
|
||||
|
||||
[[jdbc.entity-persistence.types.referenced-entities]]
|
||||
==== Referenced Entities
|
||||
|
||||
The handling of referenced entities is limited.
|
||||
This is based on the idea of aggregate roots as described above.
|
||||
If you reference another entity, that entity is, by definition, part of your aggregate.
|
||||
@@ -240,10 +241,23 @@ This also means references are 1-1 or 1-n, but not n-1 or n-m.
|
||||
|
||||
If you have n-1 or n-m references, you are, by definition, dealing with two separate aggregates.
|
||||
References between those may be encoded as simple `id` values, which map properly with Spring Data JDBC.
|
||||
A better way to encode these is to make them instances of `AggregateReference`.
|
||||
A better way to encode these, is to make them instances of `AggregateReference`.
|
||||
An `AggregateReference` is a wrapper around an id value which marks that value as a reference to a different aggregate.
|
||||
Also, the type of that aggregate is encoded in a type parameter.
|
||||
|
||||
[[jdbc.entity-persistence.types.backrefs]]
|
||||
==== Back References
|
||||
|
||||
All references in an aggregate result in a foreign key relationship in the opposite direction in the database.
|
||||
By default, the name of the foreign key column is the table name of the referencing entity, ignoring any table annotations.
|
||||
|
||||
Alternatively you may choose to have them named by the actual table name of the referencing entity.
|
||||
You activate this behaviour by calling `setForeignKeyNaming(ForeignKeyNaming.APPLY_RENAMING)` on the `RelationalMappingContext`.
|
||||
|
||||
For `List` and `Map` references an additional column is required for holding the list index or map key. It is based on the foreign key column with an additional `_KEY` suffix.
|
||||
|
||||
If you want a completely different way of naming these back references you may implement `NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)` in a way that fits your needs.
|
||||
|
||||
|
||||
.Declaring and setting an `AggregateReference`
|
||||
====
|
||||
|
||||
Reference in New Issue
Block a user