数据存储 - 使用查询和事务

datastore - using queries and transactions

我正在使用 objectify。

在我的应用程序中,我有 Employee 实体和 Task 实体。每个任务都是为给定的员工创建的。 Datastore 正在为任务实体生成 ID。

但是我有一个限制,如果该任务与另一个已经存在的任务重叠,我无法为员工保存该任务。

我不知道如何实现。我什至不知道如何开始。我应该使用交易吗?

@Entity
class Task {

    @Id
    private Long id;

    @Index
    private long startDate; // since epoch

    @Index
    private long endDate;

    @Index
    private Ref<User> createdFor;

    public Task(String id, long startDate, long endDate, User assignedTo) {

        this.id = null;
        this.startDate = startDate;
        this.endDate = endDate;
        this.createdFor = Ref.create(assignedTo);

    }

}

@Entity
class Employee {

    @Id
    private String id;

    private String name;

    public Employee(String id, String name) {

        this.id = id;
        this.name = name;

    }

}

你不能用你设置的实体来做,因为在你查询任务和插入新任务之间,你不能保证没有人已经插入了新任务与您插入的任务冲突的任务。即使您使用事务,任何同时添加的冲突任务也不会成为您事务的实体组的一部分,因此有可能违反您的约束。

你能否修改你的架构,而不是每个任务都有一个指向为其创建的员工的引用,每个员工都包含为该员工创建的任务集合?这样当您查询 Employee 的任务是否有冲突时,Employee 将在您的事务的实体组中加上时间戳,如果其他人在您完成新任务之前将新任务放入其中,则会抛出并发修改异常,您将然后重试。但是,是的,将您的查询和放入同一个事务中。

在此处阅读有关事务、实体组和乐观并发的信息:https://code.google.com/p/objectify-appengine/wiki/Concepts#Transactions

就确保您的任务不重叠而言,您只需检查新任务的开始或结束日期是否在同一员工的任何先前任务的日期范围内。您还需要检查您是否设置了在上一个任务的日期范围之前开始和结束之后的新任务。我建议为每个测试使用一个 composite.and 过滤器,然后将这三个复合过滤器组合在一个 composite.or 过滤器中,这将是您最终应用的过滤器。可能有更简洁的方法,但我是这样想的:

请注意,这些过滤器不适用于我建议的新架构。也许我会删除它们。 ////////限制分配给同一员工的任务 筛选 sameEmployeeTask = 新的 FilterPredicate("createdFor", FilterOperator.EQUAL, thisCreatedFor);

/////////Check if new startDate is in range of the prior task
Filter newTaskStartBeforePriorTaskEnd =
  new FilterPredicate("endDate", FilterOperator.GREATER_THAN, thisStartDate);

Filter newTaskStartAfterPriorTaskStart =
  new FilterPredicate("startDate", FilterOperator.LESS_THAN, thisStartDate);

Filter newTaskStartInPriorTaskRange =
  CompositeFilterOperator.and(sameEmployeeTask, newTaskStartBeforePriorTaskEnd, newTaskStartAfterPriorTaskStart);

/////////Check if new endDate is in range of the prior task
Filter newTaskEndBeforePriorTaskEnd =
  new FilterPredicate("endDate", FilterOperator.GREATER_THAN, thisEndDate);

Filter newTaskEndAfterPriorTaskStart =
  new FilterPredicate("startDate", FilterOperator.LESS_THAN, thisEndDate);

Filter newTaskEndInPriorTaskRange =
  CompositeFilterOperator.and(sameEmployeeTask, newTaskEndBeforePriorTaskEnd, newTaskEndAfterPriorTaskStart);

/////////Check if this Task overlaps the prior one on both sides
Filter newTaskStartBeforePriorTaskStart =
  new FilterPredicate("startDate", FilterOperator.GREATER_THAN_OR_EQUAL, thisStartDate);

Filter newTaskEndAfterPriorTaskEnd =
  new FilterPredicate("endDate", FilterOperator.LESS_THAN_OR_EQUAL, thisEndDate);

Filter PriorTaskRangeWithinNewTaskStartEnd = CompositeFilterOperator.and(sameEmployeeTask ,newTaskStartBeforePriorTaskStart, newTaskEndAfterPriorTaskEnd);

/////////Combine them to test if any of the three returns one or more tasks
Filter newTaskOverlapPriorTask =    CompositeFilterOperator.or(newTaskStartInPriorTaskRange,newTaskEndInPriorTaskRange,PriorTaskRangeWithinNewTaskStartEnd);

/////////Proceed
Query q = new Query("Task").setFilter(newTaskOverlapPriorTask);  

PreparedQuery pq = datastore.prepare(q);

如果您没有 return 任何结果,那么您就没有任何重叠,所以请继续并保存新任务。

好的,我希望我能提供更多帮助。我试图编辑您的问题并将您的实体更改为正确的架构。我已经为您的员工添加了一个嵌入式任务集合和一个 attemptAdd 方法。我已经为您的任务和您的员工添加了一个 detectOverlap 方法。有了这些,您就可以使用类似下面交易的东西。您将需要处理由于任务冲突而无法添加任务的情况,以及由于 ConcurrentModificationException 而导致添加失败的情况。但是你可以从中提出另一个问题,同时你应该有你需要的开始。

改编自:https://code.google.com/p/objectify-appengine/wiki/Transactions

Task myTask = new Task(startDate,endDate,description);

public boolean assignTaskToEmployee(EmployeeId, myTask) {
    ofy().transact(new VoidWork() {
        public void vrun() {            
            Employee assignee = ofy().load().key(EmployeeId).now());
            boolean taskAdded = assignee.attemptAdd(myTask);
            ofy().save().entity(assignee);
            return taskAdded;
        }
    }
}