java - 在一对多关系中替换父级后删除孤儿

标签 java google-app-engine transactions google-cloud-datastore jdo

我使用 PersistenceManagerSchool 实体持久保存到数据存储区。 SchoolStudent 具有一对多关系:

@PersistenceCapable
public class School implements Serializable {
    ...
    @Persistent(mappedBy = "school") @Element(dependent = "true") private Set<Student> students = new HashSet<Student>();
    ...
    @PrimaryKey private String key;

    School(String name, Level level) {
        this.name = Objects.requireNonNull(name);
        this.level = Objects.requireNonNull(level);
        this.key = name + "_" + level;
    }
    ...
}

@PersistenceCapable
public class Student implements Serializable {
    ...
    @PrimaryKey private Key key;

    Student(String name, School school, int grade) {
        this.name = Objects.requireNonNull(name);
        this.grade = Objects.requireNonNull(grade);
        this.school = Objects.requireNonNull(school);
        key = KeyFactory.createKey(this.school.getKey(), this.getClass().getSimpleName(), name + "_" + grade + "_" + school.getName());
    }
    ...
}

假设我维持一所学校和一些学生:

School school = new School("foo", Level.MIDDLE);
Student s1 = new Student("A", school, 6);
Student s2 = new Student("B", school, 6);
Student s3 = new Student("C", school, 6);
school.addStudent(s1);
school.addStudent(s2);
school.addStudent(s3);
PMF.get().getPersistenceManager().makePersistent(school);

稍后,

School school = new School("foo", Level.MIDDLE); // Will have the same primary key as the first!
Student s1 = new Student("A", school, 6);
Student s2 = new Student("B", school, 6);
Student s3 = new Student("D", school, 6); // Note the change! C -> D
// Some code that makes s1 and s2 different from their previous state
school.addStudent(s1);
school.addStudent(s2);
school.addStudent(s3);
PMF.get().getPersistenceManager().makePersistent(school);

由于 School 每次都会具有相同的 PrimaryKey(即 "foo_middle"),因此第二个持久操作会覆盖第一个。只会有 1 School foo

幸运的是,学生 A学生 B 的更新版本已经替换了数据存储中的旧版本,因为它们的主键相同。但是,由于学生 C未销毁,数据存储区中将存在 4 Student 实体。这种不一致在我的应用程序的一个特定 View 中变得相关。如何才能最有效地摆脱这个孤儿学生?

这是我尝试过/考虑过的:

  1. 在添加新集合之前显式删除所有 School 实体(此操作将 cascade 并销毁其拥有的所有 Student 实体),而不是让 GAE处理更换。这是有问题的,因为 (1) 读/写成本较高 (2) 删除和重新添加需要保持独立,一个严格在另一个之后,所以新的 School s 也不会被删除。这或许可以通过交易来实现?
  2. 通过在保留第二个 School 之前查询所有孤立的 Student 并对照 school.getStudents() 检查它们,显式删除所有孤立的 Student。这种方法的成本很高,并且增加了很多复杂性。

另一种可能性是以这样的方式设计对象,即我可以查询所有非孤立的 Student。然后我可以定期删除孤儿,以保持较低的存储成本。但是,我不知道如何实现这样的结果,同时仍然维护在持久化时替换旧实例的 SchoolStudent(通过具有相同的主键) .

最佳答案

学生“C”没有死的原因是您只是覆盖了 foo_middle 而不是破坏它。数据库的工作方式是,当发现重复 ID 时,它会覆盖现有 ID。当您添加学生“A”和“B”时,您会看到这一点。数据库不会删除任何原始数据,只是替换其余数据(ID 除外)。根据您的用例,您有 2 个选项:

1)如果您删除的内容少于一半,您可以添加添加/更新日期字段,更新后您只需查询所有日期在当前日期之前的日期,然后删除它们。

2)如果您要删除一半以上的学生,请执行祖先查询,然后刷新所有学生。

关于java - 在一对多关系中替换父级后删除孤儿,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25436114/

相关文章:

java - 来自 sqoop.tool 的 ImportTool 不适用于参数 (org.apache.sqoop.SqoopOptions)

java - 从 Thunderbird 扩展调用 java 方法

java - 基本 Java 语法问题

python - 如何使用 Google BigQuery python API 获得超过 100,000 个响应结果?

go - 有没有支持交易的 Kafka Go 客户端?

java - 如何在构造函数之后添加组件(例如使用 mouseClicked 事件)

python - 将现有 AppEngine DataStore 的 IntegerProperty 更改为 FloatProperty

google-app-engine - 现有 Google App Engine 网站上出现 500 服务器错误

c# - 事务中的 Firebird DDL + DML 语句

testing - 使用 Jooq 进行集成测试