Просмотр исходного кода

add support for cascading deletes on dependent entities

tags/2.0.1
Jonathan Cobb 4 лет назад
Родитель
Сommit
efe8787983
3 измененных файлов: 66 добавлений и 3 удалений
  1. +8
    -0
      wizard-common/src/main/java/org/cobbzilla/wizard/model/entityconfig/EntityFieldReference.java
  2. +37
    -3
      wizard-common/src/main/java/org/cobbzilla/wizard/model/entityconfig/EntityReferences.java
  3. +21
    -0
      wizard-server/src/main/java/org/cobbzilla/wizard/server/config/PgRestServerConfiguration.java

+ 8
- 0
wizard-common/src/main/java/org/cobbzilla/wizard/model/entityconfig/EntityFieldReference.java Просмотреть файл

@@ -1,6 +1,8 @@
package org.cobbzilla.wizard.model.entityconfig;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
@@ -9,8 +11,14 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
* When the EntityFieldType of a field is 'reference', this object is also attached to the field to describe
* how to reach the reference.
*/
@NoArgsConstructor @EqualsAndHashCode(of={"entity", "field"})
public class EntityFieldReference {

public EntityFieldReference (String entity, String field) {
this.entity = entity;
this.field = field;
}

/** A special value that can be used by child entities to indicate that the lexically enclosing entity is their parent. */
public static final String REF_PARENT = ":parent";



+ 37
- 3
wizard-common/src/main/java/org/cobbzilla/wizard/model/entityconfig/EntityReferences.java Просмотреть файл

@@ -14,9 +14,7 @@ import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndexes;
import org.cobbzilla.wizard.util.ClasspathScanner;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -56,6 +54,42 @@ public class EntityReferences {
return topology.sortReversed();
}

public static boolean hasForeignKey(Class candidate, Class<? extends Identifiable> entityClass) {
if (candidate.equals(Object.class)) return false;
if (Arrays.stream(candidate.getDeclaredFields())
.filter(EntityReferences.FIELD_HAS_FK)
.anyMatch(f -> f.getAnnotation(ECForeignKey.class).entity().equals(entityClass))) {
return true;
}
return hasForeignKey(candidate.getSuperclass(), entityClass);
}

public static List<Class<? extends Identifiable>> getDependentEntities(Class<? extends Identifiable> entityClass, List<Class<? extends Identifiable>> candidates) {
return candidates.stream()
.filter(candidate -> hasForeignKey(candidate, entityClass))
.collect(Collectors.toList());
}

public static Collection<EntityFieldReference> getDependencyRefs(Class<? extends Identifiable> entityClass,
List<Class<? extends Identifiable>> candidates) {
final Set<EntityFieldReference> refs = new LinkedHashSet<>();
candidates.forEach(dep -> refs.addAll(getDependencyRefs(entityClass, dep, dep, refs)));
return refs;
}

private static Set<EntityFieldReference> getDependencyRefs(Class<? extends Identifiable> entityClass,
Class dependency,
Class dependencyOrParent,
Set<EntityFieldReference> refs) {
if (dependencyOrParent.equals(Object.class)) return refs;
Arrays.stream(dependencyOrParent.getDeclaredFields())
.filter(FIELD_HAS_FK)
.filter(f -> f.getAnnotation(ECForeignKey.class).entity().equals(entityClass))
.forEach(f -> refs.add(new EntityFieldReference(dependency.getSimpleName(), f.getName())));
refs.addAll(getDependencyRefs(entityClass, dependency, dependencyOrParent.getSuperclass(), refs));
return refs;
}

public List<String> generateConstraintSql() { return generateConstraintSql(true); }

public List<String> generateConstraintSql(boolean includeIndexes) {


+ 21
- 0
wizard-server/src/main/java/org/cobbzilla/wizard/server/config/PgRestServerConfiguration.java Просмотреть файл

@@ -16,6 +16,7 @@ import org.cobbzilla.util.string.StringUtil;
import org.cobbzilla.util.system.Command;
import org.cobbzilla.util.system.CommandResult;
import org.cobbzilla.wizard.model.Identifiable;
import org.cobbzilla.wizard.model.entityconfig.EntityFieldReference;
import org.cobbzilla.wizard.model.entityconfig.EntityReferences;
import org.springframework.context.annotation.Bean;

@@ -23,6 +24,7 @@ import javax.persistence.Transient;
import java.io.File;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@@ -37,6 +39,8 @@ import static org.cobbzilla.util.string.StringUtil.camelCaseToSnakeCase;
import static org.cobbzilla.util.string.StringUtil.checkSafeShellArg;
import static org.cobbzilla.util.system.CommandShell.exec;
import static org.cobbzilla.util.system.CommandShell.execScript;
import static org.cobbzilla.wizard.model.entityconfig.EntityReferences.getDependencyRefs;
import static org.cobbzilla.wizard.model.entityconfig.EntityReferences.getDependentEntities;

@Slf4j
public class PgRestServerConfiguration extends RestServerConfiguration implements HasDatabaseConfiguration {
@@ -324,6 +328,23 @@ public class PgRestServerConfiguration extends RestServerConfiguration implement
return reversed;
}

private Map<Class<? extends Identifiable>, List<Class<? extends Identifiable>>> dependencyCache = new ConcurrentHashMap<>();
public List<Class<? extends Identifiable>> getDependencies (Class<? extends Identifiable> entityClass) {
return dependencyCache.computeIfAbsent(entityClass, c -> getDependentEntities(entityClass, getEntityClassesReverse()));
}

private Map<Class<? extends Identifiable>, Collection<EntityFieldReference>> dependencyDAOCache = new ConcurrentHashMap<>();
public Collection<EntityFieldReference> dependencyRefs(Class<? extends Identifiable> entityClass) {
return dependencyDAOCache.computeIfAbsent(entityClass, c -> getDependencyRefs(c, getDependencies(c)));
}

public void deleteDependencies (Identifiable thing) {
final String[] uuidArg = {thing.getUuid()};
dependencyRefs(thing.getClass()).forEach(
dep -> execSql("DELETE FROM " + camelCaseToSnakeCase(dep.getEntity()) + " WHERE " + camelCaseToSnakeCase(dep.getField()) + " = ?", uuidArg)
);
}

@Getter(lazy=true) private final List<NameAndValue> sortedSimpleEntityClassMap = initSortedSimpleEntityClassMap();
private List<NameAndValue> initSortedSimpleEntityClassMap() {
return getEntityClasses().stream()


Загрузка…
Отмена
Сохранить