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

时间:2015-06-07 03:56:29

标签: google-app-engine google-cloud-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;

    }

}

2 个答案:

答案 0 :(得分:2)

您无法对已设置的实体执行此操作,因为在查询任务和插入新任务之间,您无法保证某人不会已插入新任务与你插入的那个冲突。即使您使用交易,任何同时添加的,冲突的任务也不会成为交易实体组的一部分,因此可能会违反您的约束。

您是否可以修改您的体系结构,以便每个任务都包含为该Employee创建的任务集合,而不是每个任务都为其创建的员工提供参考?这样,当您查询Employee的冲突任务时,Employee将在您的事务的实体组中加上时间戳,如果其他人在您完成新任务之前将新任务放入其中,则会抛出并发修改异常,您会然后重试。但是,请将您的查询和您的信息放在同一个交易中。

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

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

请注意,这些过滤器不适用于我建议的新架构。也许我会删除它们。     ////////限制分配给同一员工的任务     过滤sameEmployeeTask =     new 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);

如果您没有返回任何结果,那么您没有任何重叠,因此请继续保存新任务。

答案 1 :(得分:0)

好的,我希望我能有所帮助。我试图编辑您的问题并将您的实体更改为正确的架构。我已经为您的Employee添加了一个嵌入式任务集合和一个attemptAdd方法。我已经为你的Task和你的Employee添加了一个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;
        }
    }
}