Browse Source

DATAJDBC-221 - Added AggregateReference for references across aggregates.

AggregateReferences can be used to reference other aggregates via their aggregate root without making them part of the referencing aggregate.
I.e. the referenced entities will not be included in SQL statements for the referencing aggregates, apart from their id.

Conversion between AggregateReferences and their ids and vice versa is done by the RelationalConverter.
pull/94/merge
Jens Schauder 8 years ago committed by Greg Turnquist
parent
commit
577d7643dd
No known key found for this signature in database
GPG Key ID: CB2FA4D512B5C413
  1. 11
      src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java
  2. 4
      src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java
  3. 63
      src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java
  4. 14
      src/main/java/org/springframework/data/relational/core/conversion/BasicRelationalConverter.java
  5. 30
      src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java
  6. 6
      src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java
  7. 11
      src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java
  8. 24
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java
  9. 121
      src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCrossAggregateHsqlIntegrationTests.java
  10. 77
      src/test/java/org/springframework/data/relational/core/conversion/BasicRelationalConverterAggregateReferenceUnitTests.java
  11. 37
      src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java
  12. 1
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCrossAggregateHsqlIntegrationTests-hsql.sql

11
src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java

@ -285,13 +285,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
persistentEntity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> { persistentEntity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> {
if (!property.isEntity()) { if (property.isEntity()) {
return;
}
Object value = propertyAccessor.getProperty(property); Object value = propertyAccessor.getProperty(property);
Object convertedValue = converter.writeValue(value, ClassTypeInformation.from(property.getColumnType()));
parameters.addValue(property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
Object convertedValue = converter.writeValue(value, ClassTypeInformation.from(property.getColumnType()));
parameters.addValue(property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
}
}); });
return parameters; return parameters;

4
src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java

@ -99,7 +99,9 @@ public class EntityRowMapper<T> implements RowMapper<T> {
} else { } else {
propertyAccessor.setProperty(property, readFrom(resultSet, property, "")); final Object value = readFrom(resultSet, property, "");
propertyAccessor.setProperty(property, value);
} }
} }

63
src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java

@ -0,0 +1,63 @@
/*
* Copyright 2018 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.mapping;
import lombok.RequiredArgsConstructor;
import org.springframework.lang.Nullable;
/**
* A reference to the aggregate root of a different aggregate.
*
* @param <T> the type of the referenced aggregate root.
* @param <ID> the type of the id of the referenced aggregate root.
*
* @author Jens Schauder
*
* @since 1.0
*/
public interface AggregateReference<T, ID> {
static <T, ID> AggregateReference<T, ID> to(ID id) {
return new IdOnlyAggregateReference<>(id);
}
/**
* @return the id of the referenced aggregate. May be {@code null}.
*/
@Nullable
ID getId();
/**
* An {@link AggregateReference} that only holds the id of the referenced aggregate root.
*
* Note that there is no check that a matching aggregate for this id actually exists.
*
* @param <T>
* @param <ID>
*/
@RequiredArgsConstructor
class IdOnlyAggregateReference<T, ID> implements AggregateReference<T, ID> {
private final ID id;
@Override
public ID getId() {
return id;
}
}
}

14
src/main/java/org/springframework/data/relational/core/conversion/BasicRelationalConverter.java

@ -27,6 +27,7 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.CustomConversions.StoreConversions; import org.springframework.data.convert.CustomConversions.StoreConversions;
import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyAccessor;
@ -49,6 +50,7 @@ import org.springframework.util.ClassUtils;
* Conversion is configurable by providing a customized {@link CustomConversions}. * Conversion is configurable by providing a customized {@link CustomConversions}.
* *
* @author Mark Paluch * @author Mark Paluch
* @author Jens Schauder
* @see MappingContext * @see MappingContext
* @see SimpleTypeHolder * @see SimpleTypeHolder
* @see CustomConversions * @see CustomConversions
@ -157,6 +159,14 @@ public class BasicRelationalConverter implements RelationalConverter {
return conversionService.convert(value, type.getType()); return conversionService.convert(value, type.getType());
} }
if (AggregateReference.class.isAssignableFrom(type.getType())) {
TypeInformation<?> idType = type.getSuperTypeInformation(AggregateReference.class)
.getTypeArguments().get(1);
return AggregateReference.to(readValue(value, idType));
}
return getPotentiallyConvertedSimpleRead(value, type.getType()); return getPotentiallyConvertedSimpleRead(value, type.getType());
} }
@ -172,6 +182,10 @@ public class BasicRelationalConverter implements RelationalConverter {
return null; return null;
} }
if (AggregateReference.class.isAssignableFrom(value.getClass())) {
return writeValue (((AggregateReference) value).getId(), type);
}
Class<?> rawType = type.getType(); Class<?> rawType = type.getType();
RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(value.getClass()); RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(value.getClass());

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

@ -23,6 +23,8 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.mapping.Association; import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
@ -96,6 +98,16 @@ class BasicRelationalPersistentProperty extends AnnotationBasedPersistentPropert
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public boolean isEntity() {
return super.isEntity() && !isReference();
}
@Override
public boolean isReference() {
return AggregateReference.class.isAssignableFrom(getRawType());
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName() * @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName()
@ -114,11 +126,20 @@ class BasicRelationalPersistentProperty extends AnnotationBasedPersistentPropert
@Override @Override
public Class getColumnType() { public Class getColumnType() {
if (isReference()) {
return columnTypeForReference();
}
Class columnType = columnTypeIfEntity(getActualType()); Class columnType = columnTypeIfEntity(getActualType());
return columnType == null ? columnTypeForNonEntity(getActualType()) : columnType; return columnType == null ? columnTypeForNonEntity(getActualType()) : columnType;
} }
@Override
public int getSqlType() {
return JdbcUtil.sqlTypeFor(getColumnType());
}
@Override @Override
public RelationalPersistentEntity<?> getOwner() { public RelationalPersistentEntity<?> getOwner() {
return (RelationalPersistentEntity<?>) super.getOwner(); return (RelationalPersistentEntity<?>) super.getOwner();
@ -178,4 +199,13 @@ class BasicRelationalPersistentProperty extends AnnotationBasedPersistentPropert
.findFirst() // .findFirst() //
.orElseGet(() -> ClassUtils.resolvePrimitiveIfNecessary(type)); .orElseGet(() -> ClassUtils.resolvePrimitiveIfNecessary(type));
} }
private Class columnTypeForReference() {
Class<?> componentType = getTypeInformation().getRequiredComponentType().getType();
RelationalPersistentEntity<?> referencedEntity = context.getRequiredPersistentEntity(componentType);
return referencedEntity.getRequiredIdProperty().getColumnType();
}
} }

6
src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java

@ -17,6 +17,7 @@ package org.springframework.data.relational.core.mapping;
import lombok.Getter; import lombok.Getter;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
@ -78,4 +79,9 @@ public class RelationalMappingContext
RelationalPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) { RelationalPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new BasicRelationalPersistentProperty(property, owner, simpleTypeHolder, this); return new BasicRelationalPersistentProperty(property, owner, simpleTypeHolder, this);
} }
@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
return super.shouldCreatePersistentEntityFor(type) && !AggregateReference.class.isAssignableFrom(type.getType());
}
} }

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

@ -15,6 +15,7 @@
*/ */
package org.springframework.data.relational.core.mapping; package org.springframework.data.relational.core.mapping;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -26,6 +27,8 @@ import org.springframework.lang.Nullable;
*/ */
public interface RelationalPersistentProperty extends PersistentProperty<RelationalPersistentProperty> { public interface RelationalPersistentProperty extends PersistentProperty<RelationalPersistentProperty> {
boolean isReference();
/** /**
* Returns the name of the column backing this property. * Returns the name of the column backing this property.
* *
@ -40,6 +43,14 @@ public interface RelationalPersistentProperty extends PersistentProperty<Relatio
*/ */
Class<?> getColumnType(); Class<?> getColumnType();
/**
* The SQL type constant used when using this property as a parameter for a SQL statement.
* @return Must not be {@code null}.
*
* @see java.sql.Types
*/
int getSqlType();
@Override @Override
RelationalPersistentEntity<?> getOwner(); RelationalPersistentEntity<?> getOwner();

24
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.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils;
import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.NamingStrategy;
@ -68,6 +69,7 @@ public class SqlGeneratorUnitTests {
.startsWith("SELECT") // .startsWith("SELECT") //
.contains("dummy_entity.x_id AS x_id,") // .contains("dummy_entity.x_id AS x_id,") //
.contains("dummy_entity.x_name AS x_name,") // .contains("dummy_entity.x_name AS x_name,") //
.contains("dummy_entity.x_other AS x_other,") //
.contains("ref.x_l1id AS ref_x_l1id") // .contains("ref.x_l1id AS ref_x_l1id") //
.contains("ref.x_content AS ref_x_content").contains(" FROM dummy_entity") // .contains("ref.x_content AS ref_x_content").contains(" FROM dummy_entity") //
// 1-N relationships do not get loaded via join // 1-N relationships do not get loaded via join
@ -139,9 +141,10 @@ public class SqlGeneratorUnitTests {
// this would get called when DummyEntity is the element type of a Set // this would get called when DummyEntity is the element type of a Set
String sql = sqlGenerator.getFindAllByProperty("back-ref", null, false); String sql = sqlGenerator.getFindAllByProperty("back-ref", null, false);
assertThat(sql).isEqualTo("SELECT dummy_entity.x_id AS x_id, dummy_entity.x_name AS x_name, " assertThat(sql).isEqualTo("SELECT dummy_entity.x_id AS x_id, dummy_entity.x_name AS x_name, " //
+ "ref.x_l1id AS ref_x_l1id, ref.x_content AS ref_x_content, ref.x_further AS ref_x_further " + "dummy_entity.x_other AS x_other, " //
+ "FROM dummy_entity LEFT OUTER JOIN referenced_entity AS ref ON ref.dummy_entity = dummy_entity.x_id " + "ref.x_l1id AS ref_x_l1id, ref.x_content AS ref_x_content, ref.x_further AS ref_x_further " //
+ "FROM dummy_entity LEFT OUTER JOIN referenced_entity AS ref ON ref.dummy_entity = dummy_entity.x_id " //
+ "WHERE back-ref = :back-ref"); + "WHERE back-ref = :back-ref");
} }
@ -152,6 +155,7 @@ public class SqlGeneratorUnitTests {
String sql = sqlGenerator.getFindAllByProperty("back-ref", "key-column", false); String sql = sqlGenerator.getFindAllByProperty("back-ref", "key-column", false);
assertThat(sql).isEqualTo("SELECT dummy_entity.x_id AS x_id, dummy_entity.x_name AS x_name, " // assertThat(sql).isEqualTo("SELECT dummy_entity.x_id AS x_id, dummy_entity.x_name AS x_name, " //
+ "dummy_entity.x_other AS x_other, " //
+ "ref.x_l1id AS ref_x_l1id, ref.x_content AS ref_x_content, ref.x_further AS ref_x_further, " // + "ref.x_l1id AS ref_x_l1id, ref.x_content AS ref_x_content, ref.x_further AS ref_x_further, " //
+ "dummy_entity.key-column AS key-column " // + "dummy_entity.key-column AS key-column " //
+ "FROM dummy_entity LEFT OUTER JOIN referenced_entity AS ref ON ref.dummy_entity = dummy_entity.x_id " // + "FROM dummy_entity LEFT OUTER JOIN referenced_entity AS ref ON ref.dummy_entity = dummy_entity.x_id " //
@ -169,10 +173,11 @@ public class SqlGeneratorUnitTests {
// this would get called when DummyEntity is th element type of a Map // this would get called when DummyEntity is th element type of a Map
String sql = sqlGenerator.getFindAllByProperty("back-ref", "key-column", true); String sql = sqlGenerator.getFindAllByProperty("back-ref", "key-column", true);
assertThat(sql).isEqualTo("SELECT dummy_entity.x_id AS x_id, dummy_entity.x_name AS x_name, " assertThat(sql).isEqualTo("SELECT dummy_entity.x_id AS x_id, dummy_entity.x_name AS x_name, " //
+ "ref.x_l1id AS ref_x_l1id, ref.x_content AS ref_x_content, ref.x_further AS ref_x_further, " + "dummy_entity.x_other AS x_other, " //
+ "dummy_entity.key-column AS key-column " + "ref.x_l1id AS ref_x_l1id, ref.x_content AS ref_x_content, ref.x_further AS ref_x_further, " //
+ "FROM dummy_entity LEFT OUTER JOIN referenced_entity AS ref ON ref.dummy_entity = dummy_entity.x_id " + "dummy_entity.key-column AS key-column " //
+ "FROM dummy_entity LEFT OUTER JOIN referenced_entity AS ref ON ref.dummy_entity = dummy_entity.x_id " //
+ "WHERE back-ref = :back-ref " + "ORDER BY key-column"); + "WHERE back-ref = :back-ref " + "ORDER BY key-column");
} }
@ -222,6 +227,7 @@ public class SqlGeneratorUnitTests {
ReferencedEntity ref; ReferencedEntity ref;
Set<Element> elements; Set<Element> elements;
Map<Integer, Element> mappedElements; Map<Integer, Element> mappedElements;
AggregateReference<OtherAggregate, Long> other;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -250,7 +256,11 @@ public class SqlGeneratorUnitTests {
} }
static class NoIdChild { static class NoIdChild {
}
static class OtherAggregate {
@Id Long id;
String name;
} }
private static class PrefixingNamingStrategy implements NamingStrategy { private static class PrefixingNamingStrategy implements NamingStrategy {

121
src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCrossAggregateHsqlIntegrationTests.java

@ -0,0 +1,121 @@
/*
* Copyright 2017-2018 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.repository;
import static org.assertj.core.api.Assertions.*;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.core.mapping.AggregateReference;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.transaction.annotation.Transactional;
/**
* Very simple use cases for creation and usage of JdbcRepositories.
*
* @author Jens Schauder
*/
@ContextConfiguration
@Transactional
public class JdbcRepositoryCrossAggregateHsqlIntegrationTests {
private static final long TWO_ID = 23L;
@Configuration
@Import(TestConfiguration.class)
@EnableJdbcRepositories(considerNestedRepositories = true)
static class Config {
@Autowired JdbcRepositoryFactory factory;
@Bean
Class<?> testClass() {
return JdbcRepositoryCrossAggregateHsqlIntegrationTests.class;
}
}
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Autowired NamedParameterJdbcTemplate template;
@Autowired Ones ones;
@Autowired RelationalMappingContext context;
@SuppressWarnings("ConstantConditions")
@Test // DATAJDBC-221
public void savesAndRead() {
AggregateOne one = new AggregateOne();
one.name = "Aggregate - 1";
one.two = AggregateReference.to(TWO_ID);
one = ones.save(one);
AggregateOne reloaded = ones.findById(one.id).get();
assertThat(reloaded.two.getId()).isEqualTo(TWO_ID);
}
@Test // DATAJDBC-221
public void savesAndUpdate() {
AggregateOne one = new AggregateOne();
one.name = "Aggregate - 1";
one.two = AggregateReference.to(42L);
one = ones.save(one);
one.two = AggregateReference.to(TWO_ID);
ones.save(one);
assertThat( //
JdbcTestUtils.countRowsInTableWhere( //
(JdbcTemplate) template.getJdbcOperations(), //
"aggregate_one", //
"two = " + TWO_ID) //
).isEqualTo(1);
}
interface Ones extends CrudRepository<AggregateOne, Long> {}
static class AggregateOne {
@Id Long id;
String name;
AggregateReference<AggregateTwo, Long> two;
}
static class AggregateTwo {
@Id Long id;
String name;
}
}

77
src/test/java/org/springframework/data/relational/core/conversion/BasicRelationalConverterAggregateReferenceUnitTests.java

@ -0,0 +1,77 @@
/*
* Copyright 2018 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.relational.core.conversion;
import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
/**
* Unit tests for the handling of {@link AggregateReference}s in the
* {@link org.springframework.data.relational.core.conversion.BasicRelationalConverter}.
*
* @author Jens Schauder
*/
public class BasicRelationalConverterAggregateReferenceUnitTests {
SoftAssertions softly = new SoftAssertions();
ConversionService conversionService = new DefaultConversionService();
RelationalMappingContext context = new RelationalMappingContext();
RelationalConverter converter = new BasicRelationalConverter(context);
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(DummyEntity.class);
@Test // DATAJDBC-221
public void convertsToAggregateReference() {
final RelationalPersistentProperty property = entity.getRequiredPersistentProperty("reference");
Object readValue = converter.readValue(23, property.getTypeInformation());
assertThat(readValue).isInstanceOf(AggregateReference.class);
assertThat(((AggregateReference<DummyEntity, Long>) readValue).getId()).isEqualTo(23L);
}
@Test // DATAJDBC-221
public void convertsFromAggregateReference() {
final RelationalPersistentProperty property = entity.getRequiredPersistentProperty("reference");
AggregateReference<Object, Integer> reference = AggregateReference.to(23);
Object writeValue = converter.writeValue(reference, ClassTypeInformation.from(property.getColumnType()));
assertThat(writeValue).isEqualTo(23L);
}
private static class DummyEntity {
@Id
Long simple;
AggregateReference<DummyEntity, Long> reference;
}
}

37
src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java

@ -27,6 +27,8 @@ import java.util.UUID;
import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.SoftAssertions;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.PropertyHandler;
/** /**
@ -39,13 +41,14 @@ import org.springframework.data.mapping.PropertyHandler;
public class BasicRelationalPersistentPropertyUnitTests { public class BasicRelationalPersistentPropertyUnitTests {
RelationalMappingContext context = new RelationalMappingContext(); RelationalMappingContext context = new RelationalMappingContext();
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(DummyEntity.class);
@Test // DATAJDBC-104 @Test // DATAJDBC-104
public void enumGetsStoredAsString() { public void enumGetsStoredAsString() {
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
persistentEntity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) p -> { entity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) p -> {
switch (p.getName()) { switch (p.getName()) {
case "someEnum": case "someEnum":
assertThat(p.getColumnType()).isEqualTo(String.class); assertThat(p.getColumnType()).isEqualTo(String.class);
@ -90,13 +93,26 @@ public class BasicRelationalPersistentPropertyUnitTests {
public void detectsAnnotatedColumnAndKeyName() { public void detectsAnnotatedColumnAndKeyName() {
RelationalPersistentProperty listProperty = context // RelationalPersistentProperty listProperty = context //
.getRequiredPersistentEntity(DummyEntity.class) // .getRequiredPersistentEntity(DummyEntity.class) //
.getRequiredPersistentProperty("someList"); .getRequiredPersistentProperty("someList");
assertThat(listProperty.getReverseColumnName()).isEqualTo("dummy_column_name"); assertThat(listProperty.getReverseColumnName()).isEqualTo("dummy_column_name");
assertThat(listProperty.getKeyColumn()).isEqualTo("dummy_key_column_name"); assertThat(listProperty.getKeyColumn()).isEqualTo("dummy_key_column_name");
} }
@Test // DATAJDBC-221
public void referencesAreNotEntitiesAndGetStoredAsTheirId() {
SoftAssertions softly = new SoftAssertions();
RelationalPersistentProperty reference = entity.getRequiredPersistentProperty("reference");
softly.assertThat(reference.isEntity()).isFalse();
softly.assertThat(reference.getColumnType()).isEqualTo(Long.class);
softly.assertAll();
}
private void checkTargetType(SoftAssertions softly, RelationalPersistentEntity<?> persistentEntity, private void checkTargetType(SoftAssertions softly, RelationalPersistentEntity<?> persistentEntity,
String propertyName, Class<?> expected) { String propertyName, Class<?> expected) {
@ -106,11 +122,15 @@ public class BasicRelationalPersistentPropertyUnitTests {
} }
@Data @Data
@SuppressWarnings("unused")
private static class DummyEntity { private static class DummyEntity {
@Id private final Long id;
private final SomeEnum someEnum; private final SomeEnum someEnum;
private final LocalDateTime localDateTime; private final LocalDateTime localDateTime;
private final ZonedDateTime zonedDateTime; private final ZonedDateTime zonedDateTime;
private final AggregateReference<DummyEntity, Long> reference;
private final List<String> listField;
private final UUID uuid; private final UUID uuid;
@Column(value = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList; @Column(value = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList;
@ -122,9 +142,18 @@ public class BasicRelationalPersistentPropertyUnitTests {
public LocalDateTime getLocalDateTime() { public LocalDateTime getLocalDateTime() {
return localDateTime; return localDateTime;
} }
public void setListSetter(Integer integer) {
}
public List<Date> getListGetter() {
return null;
}
} }
@SuppressWarnings("unused")
private enum SomeEnum { private enum SomeEnum {
ALPHA; ALPHA
} }
} }

1
src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCrossAggregateHsqlIntegrationTests-hsql.sql

@ -0,0 +1 @@
CREATE TABLE aggregate_one ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100), two INTEGER);
Loading…
Cancel
Save