Skip to content

Commit

Permalink
Introduce AggregatePath.
Browse files Browse the repository at this point in the history
AggregatePath replaces PersistentPropertyPathExtension.
It gets created and cached by the RelationalMappingContext, which should be more efficient and certainly looks nicer.

Closes #1525
Original pull request #1486
  • Loading branch information
schauder committed Jul 21, 2023
1 parent 7d5bc1c commit d00e928
Show file tree
Hide file tree
Showing 44 changed files with 1,784 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,7 @@
*/
package org.springframework.data.jdbc.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

Expand All @@ -38,11 +29,12 @@
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.relational.core.conversion.DbAction;
import org.springframework.data.relational.core.conversion.DbActionExecutionResult;
import org.springframework.data.relational.core.conversion.IdValueSource;
import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
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.relational.core.sql.LockMode;
Expand All @@ -65,7 +57,7 @@ class JdbcAggregateChangeExecutionContext {
private static final String UPDATE_FAILED = "Failed to update entity [%s]; Id [%s] not found in database";
private static final String UPDATE_FAILED_OPTIMISTIC_LOCKING = "Failed to update entity [%s]; The entity was updated since it was rea or it isn't in the database at all";

private final MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> context;
private final RelationalMappingContext context;
private final JdbcConverter converter;
private final DataAccessStrategy accessStrategy;

Expand Down Expand Up @@ -184,39 +176,34 @@ private Identifier getParentKeys(DbAction.WithDependingOn<?> action, JdbcConvert
Object id = getParentId(action);

JdbcIdentifierBuilder identifier = JdbcIdentifierBuilder //
.forBackReferences(converter, new PersistentPropertyPathExtension(context, action.getPropertyPath()), id);
.forBackReferences(converter, context.getAggregatePath(action.getPropertyPath()), id);

for (Map.Entry<PersistentPropertyPath<RelationalPersistentProperty>, Object> qualifier : action.getQualifiers()
.entrySet()) {
identifier = identifier.withQualifier(new PersistentPropertyPathExtension(context, qualifier.getKey()),
qualifier.getValue());
identifier = identifier.withQualifier(context.getAggregatePath(qualifier.getKey()), qualifier.getValue());
}

return identifier.build();
}

private Object getParentId(DbAction.WithDependingOn<?> action) {

PersistentPropertyPathExtension path = new PersistentPropertyPathExtension(context, action.getPropertyPath());
PersistentPropertyPathExtension idPath = path.getIdDefiningParentPath();

DbAction.WithEntity<?> idOwningAction = getIdOwningAction(action, idPath);
DbAction.WithEntity<?> idOwningAction = getIdOwningAction(action, context.getAggregatePath(action.getPropertyPath()).getIdDefiningParentPath());

return getPotentialGeneratedIdFrom(idOwningAction);
}

private DbAction.WithEntity<?> getIdOwningAction(DbAction.WithEntity<?> action,
PersistentPropertyPathExtension idPath) {
private DbAction.WithEntity<?> getIdOwningAction(DbAction.WithEntity<?> action, AggregatePath idPath) {

if (!(action instanceof DbAction.WithDependingOn<?> withDependingOn)) {

Assert.state(idPath.getLength() == 0,
Assert.state(idPath.isRoot(),
"When the id path is not empty the id providing action should be of type WithDependingOn");

return action;
}

if (idPath.matches(withDependingOn.getPropertyPath())) {
if (idPath.equals(context.getAggregatePath(withDependingOn.getPropertyPath()))) {
return action;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.AggregatePath;
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.relational.core.sql.IdentifierProcessing;
Expand Down Expand Up @@ -86,14 +87,14 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
* Creates a new {@link BasicRelationalConverter} given {@link MappingContext} and a
* {@link JdbcTypeFactory#unsupported() no-op type factory} throwing {@link UnsupportedOperationException} on type
* creation. Use
* {@link #BasicJdbcConverter(MappingContext, RelationResolver, CustomConversions, JdbcTypeFactory, IdentifierProcessing)}
* {@link #BasicJdbcConverter(RelationalMappingContext, RelationResolver, CustomConversions, JdbcTypeFactory, IdentifierProcessing)}
* (MappingContext, RelationResolver, JdbcTypeFactory)} to convert arrays and large objects into JDBC-specific types.
*
* @param context must not be {@literal null}.
* @param relationResolver used to fetch additional relations from the database. Must not be {@literal null}.
*/
public BasicJdbcConverter(
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> context,
RelationalMappingContext context,
RelationResolver relationResolver) {

super(context, new JdbcCustomConversions());
Expand All @@ -116,7 +117,7 @@ public BasicJdbcConverter(
* @since 2.0
*/
public BasicJdbcConverter(
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> context,
RelationalMappingContext context,
RelationResolver relationResolver, CustomConversions conversions, JdbcTypeFactory typeFactory,
IdentifierProcessing identifierProcessing) {

Expand Down Expand Up @@ -300,12 +301,13 @@ private JdbcValue tryToConvertToJdbcValue(@Nullable Object value) {

@Override
public <T> T mapRow(RelationalPersistentEntity<T> entity, ResultSet resultSet, Object key) {
return new ReadingContext<T>(new PersistentPropertyPathExtension(getMappingContext(), entity),
return new ReadingContext<T>(getMappingContext().getAggregatePath( entity),
new ResultSetAccessor(resultSet), Identifier.empty(), key).mapRow();
}


@Override
public <T> T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key) {
public <T> T mapRow(AggregatePath path, ResultSet resultSet, Identifier identifier, Object key) {
return new ReadingContext<T>(path, new ResultSetAccessor(resultSet), identifier, key).mapRow();
}

Expand Down Expand Up @@ -350,8 +352,8 @@ private class ReadingContext<T> {

private final RelationalPersistentEntity<T> entity;

private final PersistentPropertyPathExtension rootPath;
private final PersistentPropertyPathExtension path;
private final AggregatePath rootPath;
private final AggregatePath path;
private final Identifier identifier;
private final Object key;

Expand All @@ -360,26 +362,27 @@ private class ReadingContext<T> {
private final ResultSetAccessor accessor;

@SuppressWarnings("unchecked")
private ReadingContext(PersistentPropertyPathExtension rootPath, ResultSetAccessor accessor, Identifier identifier,
private ReadingContext(AggregatePath rootPath, ResultSetAccessor accessor, Identifier identifier,
Object key) {
RelationalPersistentEntity<T> entity = (RelationalPersistentEntity<T>) rootPath.getLeafEntity();

Assert.notNull(entity, "The rootPath must point to an entity");

this.entity = entity;
this.rootPath = rootPath;
this.path = new PersistentPropertyPathExtension(getMappingContext(), this.entity);
this.path = getMappingContext().getAggregatePath( this.entity);
this.identifier = identifier;
this.key = key;
this.propertyValueProvider = new JdbcPropertyValueProvider(path, accessor);
this.backReferencePropertyValueProvider = new JdbcBackReferencePropertyValueProvider(path, accessor);
this.accessor = accessor;
}

private ReadingContext(RelationalPersistentEntity<T> entity, PersistentPropertyPathExtension rootPath,
PersistentPropertyPathExtension path, Identifier identifier, Object key,
private ReadingContext(RelationalPersistentEntity<T> entity, AggregatePath rootPath,
AggregatePath path, Identifier identifier, Object key,
JdbcPropertyValueProvider propertyValueProvider,
JdbcBackReferencePropertyValueProvider backReferencePropertyValueProvider, ResultSetAccessor accessor) {

this.entity = entity;
this.rootPath = rootPath;
this.path = path;
Expand All @@ -393,7 +396,7 @@ private ReadingContext(RelationalPersistentEntity<T> entity, PersistentPropertyP
private <S> ReadingContext<S> extendBy(RelationalPersistentProperty property) {
return new ReadingContext<>(
(RelationalPersistentEntity<S>) getMappingContext().getRequiredPersistentEntity(property.getActualType()),
rootPath.extendBy(property), path.extendBy(property), identifier, key,
rootPath.append(property), path.append(property), identifier, key,
propertyValueProvider.extendBy(property), backReferencePropertyValueProvider.extendBy(property), accessor);
}

Expand Down Expand Up @@ -453,10 +456,10 @@ private Object readOrLoadProperty(@Nullable Object id, RelationalPersistentPrope
private Iterable<Object> resolveRelation(@Nullable Object id, RelationalPersistentProperty property) {

Identifier identifier = id == null //
? this.identifier.withPart(rootPath.getQualifierColumn(), key, Object.class) //
: Identifier.of(rootPath.extendBy(property).getReverseColumnName(), id, Object.class);
? this.identifier.withPart(rootPath.getTableInfo().qualifierColumnInfo().name(), key, Object.class) //
: Identifier.of(rootPath.append(property).getTableInfo().reverseColumnInfo().name(), id, Object.class);

PersistentPropertyPath<? extends RelationalPersistentProperty> propertyPath = path.extendBy(property)
PersistentPropertyPath<? extends RelationalPersistentProperty> propertyPath = path.append(property)
.getRequiredPersistentPropertyPath();

return relationResolver.findAllByPath(identifier, propertyPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.conversion.IdValueSource;
import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
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.relational.core.query.Query;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.jdbc.core.RowMapper;
Expand Down Expand Up @@ -297,8 +297,8 @@ public Iterable<Object> findAllByPath(Identifier identifier,
Assert.notNull(identifier, "identifier must not be null");
Assert.notNull(propertyPath, "propertyPath must not be null");

PersistentPropertyPathExtension path = new PersistentPropertyPathExtension(context, propertyPath);
Class<?> actualType = path.getActualType();
AggregatePath path = context.getAggregatePath(propertyPath);
Class<?> actualType = path.getLeafEntity().getType();

String findAllByProperty = sql(actualType) //
.getFindAllByProperty(identifier, propertyPath);
Expand Down Expand Up @@ -339,8 +339,7 @@ public <T> Optional<T> findOne(Query query, Class<T> domainType) {
String sqlQuery = sql(domainType).selectByQuery(query, parameterSource);

try {
return Optional.ofNullable(
operations.queryForObject(sqlQuery, parameterSource, getEntityRowMapper(domainType)));
return Optional.ofNullable(operations.queryForObject(sqlQuery, parameterSource, getEntityRowMapper(domainType)));
} catch (EmptyResultDataAccessException e) {
return Optional.empty();
}
Expand Down Expand Up @@ -394,14 +393,15 @@ private <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), converter);
}

private EntityRowMapper<?> getEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) {
private EntityRowMapper<?> getEntityRowMapper(AggregatePath path, Identifier identifier) {
return new EntityRowMapper<>(path, converter, identifier);
}

private RowMapper<?> getMapEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) {
private RowMapper<?> getMapEntityRowMapper(AggregatePath path, Identifier identifier) {

SqlIdentifier keyColumn = path.getQualifierColumn();
Assert.notNull(keyColumn, () -> "KeyColumn must not be null for " + path);
AggregatePath.ColumnInfo qualifierColumnInfo = path.getTableInfo().qualifierColumnInfo();
Assert.notNull(qualifierColumnInfo, () -> "Qualifier column must not be null for " + path);
SqlIdentifier keyColumn = qualifierColumnInfo.name();

return new MapEntityRowMapper<>(path, converter, identifier, keyColumn);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.sql.ResultSet;

import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.jdbc.core.RowMapper;
Expand All @@ -35,13 +36,28 @@
public class EntityRowMapper<T> implements RowMapper<T> {

private final RelationalPersistentEntity<T> entity;
private final PersistentPropertyPathExtension path;
private final AggregatePath path;
private final JdbcConverter converter;
private final Identifier identifier;

/**
*
*
* @deprecated use {@link EntityRowMapper#EntityRowMapper(AggregatePath, JdbcConverter, Identifier)} instead
*/
@Deprecated(since = "3.2", forRemoval = true)
@SuppressWarnings("unchecked")
public EntityRowMapper(PersistentPropertyPathExtension path, JdbcConverter converter, Identifier identifier) {

this.entity = (RelationalPersistentEntity<T>) path.getLeafEntity();
this.path = path.getAggregatePath();
this.converter = converter;
this.identifier = identifier;
}

@SuppressWarnings("unchecked")
public EntityRowMapper(AggregatePath path, JdbcConverter converter, Identifier identifier) {

this.entity = (RelationalPersistentEntity<T>) path.getLeafEntity();
this.path = path;
this.converter = converter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
package org.springframework.data.jdbc.core.convert;

import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.sql.IdentifierProcessing;

/**
* {@link PropertyValueProvider} obtaining values from a {@link ResultSetAccessor}. For a given id property it provides
Expand All @@ -31,25 +30,25 @@
*/
class JdbcBackReferencePropertyValueProvider implements PropertyValueProvider<RelationalPersistentProperty> {

private final PersistentPropertyPathExtension basePath;
private final AggregatePath basePath;
private final ResultSetAccessor resultSet;

/**
* @param basePath path from the aggregate root relative to which all properties get resolved.
* @param resultSet the {@link ResultSetAccessor} from which to obtain the actual values.
*/
JdbcBackReferencePropertyValueProvider(PersistentPropertyPathExtension basePath, ResultSetAccessor resultSet) {
JdbcBackReferencePropertyValueProvider(AggregatePath basePath, ResultSetAccessor resultSet) {

this.resultSet = resultSet;
this.basePath = basePath;
}

@Override
public <T> T getPropertyValue(RelationalPersistentProperty property) {
return (T) resultSet.getObject(basePath.extendBy(property).getReverseColumnNameAlias().getReference());
return (T) resultSet.getObject(basePath.append(property).getTableInfo().reverseColumnInfo().alias().getReference());
}

public JdbcBackReferencePropertyValueProvider extendBy(RelationalPersistentProperty property) {
return new JdbcBackReferencePropertyValueProvider(basePath.extendBy(property), resultSet);
return new JdbcBackReferencePropertyValueProvider(basePath.append(property), resultSet);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
import java.sql.SQLType;

import org.springframework.data.jdbc.core.mapping.JdbcValue;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
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.TypeInformation;
Expand Down Expand Up @@ -67,8 +70,24 @@ public interface JdbcConverter extends RelationalConverter {
* @param key primary key.
* @param <T>
* @return
* @deprecated use {@link #mapRow(AggregatePath, ResultSet, Identifier, Object)} instead.
*/
<T> T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key);
@Deprecated(since = "3.2", forRemoval = true)
default <T> T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key){
return mapRow(path.getAggregatePath(), resultSet, identifier, key);
};

/**
* Read the current row from {@link ResultSet} to an {@link AggregatePath#getLeafEntity()} entity}.
*
* @param path path to the owning property.
* @param resultSet the {@link ResultSet} to read from.
* @param identifier entity identifier.
* @param key primary key.
* @param <T>
* @return
*/
<T> T mapRow(AggregatePath path, ResultSet resultSet, Identifier identifier, Object key);

/**
* The type to be used to store this property in the database. Multidimensional arrays are unwrapped to reflect a
Expand All @@ -88,4 +107,7 @@ public interface JdbcConverter extends RelationalConverter {
* @since 2.0
*/
SQLType getTargetSqlType(RelationalPersistentProperty property);

@Override
RelationalMappingContext getMappingContext();
}
Loading

0 comments on commit d00e928

Please sign in to comment.