Я делаю проект, и мне нужно реализовать некоторые денормализации. В частности, мне нужно запросить проекты по taskCount и / или projectTotalCost .
Это то, что я имею до сих пор.
Мне кажется, это работает, но я больше не уверен. Боюсь, что он подвержен гоночным условиям и тому подобному. В частности, части, где я обновляю счетчики.
Несмотря на то, что я использую транзакции внутри моего сервиса, я не уверен насчет транзакции IsolationLevels.
Это нормально ? Нужно ли добавить что-то еще? Должен ли я использовать @Version (кроме транзакций)?
Мне действительно нужно поддерживать эту денормализацию (taskCount, projectTotalCost) и поддерживать ее согласованной.
@Entity
@Data
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "project_id", referencedColumnName = "id")
private List<Task> tasks;
private int taskCount;
private double projectTotalCost;
public Project() {
this.tasks = new ArrayList<>();
this.taskCount = 0;
this.projectTotalCost = 0;
}
public void addTask(Task t) {
this.tasks.add(t);
updateTaskCount();
updateProjectCost();
}
public void removeTask(Task t) {
this.tasks.remove(t);
updateTaskCount();
updateProjectCost();
}
public void removeWhere(Predicate<Task> spec) {
this.tasks.removeIf(spec);
updateTaskCount();
updateProjectCost();
}
private void updateTaskCount() {
this.taskCount = this.tasks.size();
}
private void updateProjectCost() {
this.projectTotalCost = this.tasks.stream().mapToDouble((Task t) -> t.getCost()).sum();
}
}
@Entity
@Data
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private double cost;
public Task(String name, String description, double cost) {
this.name = name;
this.description = description;
this.cost = cost;
}
public double getCost() {
return this.cost;
}
}
@Service
public class ProjectService {
@Autowired
private ProjectRepository repo;
@Transactional
public Project create(Project p) {
return this.repo.save(p);
}
@Transactional
public Project addTask(long projectId, Task t) {
Project p = this.repo.findById(projectId);
p.addTask(t);
return repo.save(p);
}
@Transactional
public Project removeTaskFromProject(long projectId, long taskId) {
Project p = this.repo.findById(projectId);
p.removeWhere((Task t) -> t.hasId(taskId));
return repo.save(p);
}
}