org.springframework.data.relational.core.conversion.DbAction Maven / Gradle / Ivy
Show all versions of spring-data-relational Show documentation
/*
* Copyright 2018-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.conversion;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.util.Pair;
import org.springframework.lang.Nullable;
/**
* An instance of this interface represents a (conceptual) single interaction with a database, e.g. a single update,
* used as a step when synchronizing the state of an aggregate with the database.
*
* @param the type of the entity that is affected by this action.
* @author Jens Schauder
* @author Mark Paluch
* @author Tyler Van Gorder
* @author Myeonghyeon Lee
*/
public interface DbAction {
Class getEntityType();
/**
* Represents an insert statement for a single entity that is not the root of an aggregate.
*
* @param type of the entity for which this represents a database interaction.
*/
class Insert implements WithGeneratedId, WithDependingOn {
private final T entity;
private final PersistentPropertyPath propertyPath;
private final WithEntity dependingOn;
final Map, Object> qualifiers;
public Insert(T entity, PersistentPropertyPath propertyPath,
WithEntity dependingOn, Map, Object> qualifiers) {
this.entity = entity;
this.propertyPath = propertyPath;
this.dependingOn = dependingOn;
this.qualifiers = Collections.unmodifiableMap(new HashMap<>(qualifiers));
}
@Override
public Class getEntityType() {
return WithDependingOn.super.getEntityType();
}
public T getEntity() {
return this.entity;
}
public PersistentPropertyPath getPropertyPath() {
return this.propertyPath;
}
public DbAction.WithEntity getDependingOn() {
return this.dependingOn;
}
public Map, Object> getQualifiers() {
return this.qualifiers;
}
public String toString() {
return "DbAction.Insert(entity=" + this.getEntity() + ", propertyPath=" + this.getPropertyPath()
+ ", dependingOn=" + this.getDependingOn() + ", qualifiers=" + this.getQualifiers() + ")";
}
}
/**
* Represents an insert statement for the root of an aggregate. Upon a successful insert, the initial version and
* generated ids are populated.
*
* @param type of the entity for which this represents a database interaction.
*/
class InsertRoot implements WithGeneratedId {
private final T entity;
public InsertRoot(T entity) {
this.entity = entity;
}
public T getEntity() {
return this.entity;
}
public String toString() {
return "DbAction.InsertRoot(entity=" + this.getEntity() + ")";
}
}
/**
* Represents an update statement for a single entity that is not the root of an aggregate.
*
* @param type of the entity for which this represents a database interaction.
*/
final class Update implements WithEntity {
private final T entity;
private final PersistentPropertyPath propertyPath;
public Update(T entity, PersistentPropertyPath propertyPath) {
this.entity = entity;
this.propertyPath = propertyPath;
}
public T getEntity() {
return this.entity;
}
public PersistentPropertyPath getPropertyPath() {
return this.propertyPath;
}
public String toString() {
return "DbAction.Update(entity=" + this.getEntity() + ", propertyPath=" + this.getPropertyPath() + ")";
}
}
/**
* Represents an update statement for the aggregate root.
*
* @param type of the entity for which this represents a database interaction.
*/
class UpdateRoot implements WithEntity {
private final T entity;
public UpdateRoot(T entity) {
this.entity = entity;
}
public T getEntity() {
return this.entity;
}
public String toString() {
return "DbAction.UpdateRoot(entity=" + this.getEntity() + ")";
}
}
/**
* Represents a merge statement for a single entity that is not the root of an aggregate.
*
* @param type of the entity for which this represents a database interaction.
*/
final class Merge implements WithDependingOn, WithPropertyPath {
private final T entity;
private final PersistentPropertyPath propertyPath;
private final WithEntity dependingOn;
private final Map, Object> qualifiers = Collections.emptyMap();
public Merge(T entity, PersistentPropertyPath propertyPath,
WithEntity dependingOn) {
this.entity = entity;
this.propertyPath = propertyPath;
this.dependingOn = dependingOn;
}
public T getEntity() {
return this.entity;
}
public PersistentPropertyPath getPropertyPath() {
return this.propertyPath;
}
public DbAction.WithEntity getDependingOn() {
return this.dependingOn;
}
public Map, Object> getQualifiers() {
return this.qualifiers;
}
public String toString() {
return "DbAction.Merge(entity=" + this.getEntity() + ", propertyPath=" + this.getPropertyPath() + ", dependingOn="
+ this.getDependingOn() + ", qualifiers=" + this.getQualifiers() + ")";
}
}
/**
* Represents a delete statement for all entities that that a reachable via a give path from the aggregate root.
*
* @param type of the entity for which this represents a database interaction.
*/
final class Delete implements WithPropertyPath {
private final Object rootId;
private final PersistentPropertyPath propertyPath;
public Delete(Object rootId, PersistentPropertyPath propertyPath) {
this.rootId = rootId;
this.propertyPath = propertyPath;
}
public Object getRootId() {
return this.rootId;
}
public PersistentPropertyPath getPropertyPath() {
return this.propertyPath;
}
public String toString() {
return "DbAction.Delete(rootId=" + this.getRootId() + ", propertyPath=" + this.getPropertyPath() + ")";
}
}
/**
* Represents a delete statement for a aggregate root when only the ID is known.
*
* Note that deletes for contained entities that reference the root are to be represented by separate
* {@link DbAction}s.
*
* @param type of the entity for which this represents a database interaction.
*/
final class DeleteRoot implements DbAction {
private final Object id;
private final Class entityType;
@Nullable private final Number previousVersion;
public DeleteRoot(Object id, Class entityType, @Nullable Number previousVersion) {
this.id = id;
this.entityType = entityType;
this.previousVersion = previousVersion;
}
public Object getId() {
return this.id;
}
public Class getEntityType() {
return this.entityType;
}
@Nullable
public Number getPreviousVersion() {
return this.previousVersion;
}
public String toString() {
return "DbAction.DeleteRoot(id=" + this.getId() + ", entityType=" + this.getEntityType() + ", previousVersion="
+ this.getPreviousVersion() + ")";
}
}
/**
* Represents an delete statement for all entities that that a reachable via a give path from any aggregate root of a
* given type.
*
* @param type of the entity for which this represents a database interaction.
*/
final class DeleteAll implements WithPropertyPath {
private final PersistentPropertyPath propertyPath;
public DeleteAll(PersistentPropertyPath propertyPath) {
this.propertyPath = propertyPath;
}
public PersistentPropertyPath getPropertyPath() {
return this.propertyPath;
}
public String toString() {
return "DbAction.DeleteAll(propertyPath=" + this.getPropertyPath() + ")";
}
}
/**
* Represents a delete statement for all aggregate roots of a given type.
*
* Note that deletes for contained entities that reference the root are to be represented by separate
* {@link DbAction}s.
*
* @param type of the entity for which this represents a database interaction.
*/
final class DeleteAllRoot implements DbAction {
private final Class entityType;
public DeleteAllRoot(Class entityType) {
this.entityType = entityType;
}
public Class getEntityType() {
return this.entityType;
}
public String toString() {
return "DbAction.DeleteAllRoot(entityType=" + this.getEntityType() + ")";
}
}
/**
* Represents an acquire lock statement for a aggregate root when only the ID is known.
*
*
* @param type of the entity for which this represents a database interaction.
*/
final class AcquireLockRoot implements DbAction {
private final Object id;
private final Class entityType;
AcquireLockRoot(Object id, Class entityType) {
this.id = id;
this.entityType = entityType;
}
public Object getId() {
return this.id;
}
public Class getEntityType() {
return this.entityType;
}
public String toString() {
return "DbAction.AcquireLockRoot(id=" + this.getId() + ", entityType=" + this.getEntityType() + ")";
}
}
/**
* Represents an acquire lock statement for all aggregate roots of a given type.
*
* @param type of the entity for which this represents a database interaction.
*/
final class AcquireLockAllRoot implements DbAction {
private final Class entityType;
AcquireLockAllRoot(Class entityType) {
this.entityType = entityType;
}
public Class getEntityType() {
return this.entityType;
}
public String toString() {
return "DbAction.AcquireLockAllRoot(entityType=" + this.getEntityType() + ")";
}
}
/**
* An action depending on another action for providing additional information like the id of a parent entity.
*
* @author Jens Schauder
*/
interface WithDependingOn extends WithPropertyPath, WithEntity {
/**
* The {@link DbAction} of a parent entity, possibly the aggregate root. This is used to obtain values needed to
* persist the entity, that are not part of the current entity, especially the id of the parent, which might only
* become available once the parent entity got persisted.
*
* @return guaranteed to be not {@code null}.
* @see #getQualifiers()
*/
WithEntity getDependingOn();
/**
* Additional values to be set during insert or update statements.
*
* Values come from parent entities but one might also add values manually.
*
* @return guaranteed to be not {@code null}.
*/
Map, Object> getQualifiers();
// TODO: Encapsulate propertyPath and qualifier in object: PropertyPathWithListIndex,
// PropertyPathWithMapIndex, PropertyPathInSet, PropertyPathWithoutQualifier
// Probably we need better names.
@Nullable
default Pair, Object> getQualifier() {
Map, Object> qualifiers = getQualifiers();
if (qualifiers.size() == 0)
return null;
if (qualifiers.size() > 1) {
throw new IllegalStateException("Can't handle more then one qualifier");
}
Map.Entry, Object> entry = qualifiers.entrySet().iterator()
.next();
if (entry.getValue() == null) {
return null;
}
return Pair.of(entry.getKey(), entry.getValue());
}
@Override
default Class getEntityType() {
return WithEntity.super.getEntityType();
}
}
/**
* A {@link DbAction} that stores the information of a single entity in the database.
*
* @author Jens Schauder
*/
interface WithEntity extends DbAction {
/**
* @return the entity to persist. Guaranteed to be not {@code null}.
*/
T getEntity();
@SuppressWarnings("unchecked")
@Override
default Class getEntityType() {
return (Class) getEntity().getClass();
}
}
/**
* A {@link DbAction} that may "update" its entity. In order to support immutable entities this requires at least
* potentially creating a new instance, which this interface makes available.
*
* @author Jens Schauder
*/
interface WithGeneratedId extends WithEntity {
@SuppressWarnings("unchecked")
@Override
default Class getEntityType() {
return (Class) getEntity().getClass();
}
}
/**
* A {@link DbAction} not operation on the root of an aggregate but on its contained entities.
*
* @author Jens Schauder
*/
interface WithPropertyPath extends DbAction {
/**
* @return the path from the aggregate root to the affected entity
*/
PersistentPropertyPath getPropertyPath();
@SuppressWarnings("unchecked")
@Override
default Class getEntityType() {
return (Class) getPropertyPath().getRequiredLeafProperty().getActualType();
}
}
}