spring jpa denormalize one to many











up vote
0
down vote

favorite












I am doing a project and i need to implement some denormalizations. Specifically i need to query Projects by taskCount and/or projectTotalCost.
This is what i have so far.
Seems to me like it works, but i'm not sure anymore. I'm afraid it is prone to race conditions and things like that. Specifically the parts where i update the counters.
Even though i'm using transactions inside my service, i'm not sure about transaction IsolationLevels.
Is this ok ? Do i need to add something else ? Should i use @Version as well (beside transactions) ?
I really need to maintain this denormalization (taskCount, projectTotalCost) and keep it consistent.



@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);

}

}









share|improve this question


























    up vote
    0
    down vote

    favorite












    I am doing a project and i need to implement some denormalizations. Specifically i need to query Projects by taskCount and/or projectTotalCost.
    This is what i have so far.
    Seems to me like it works, but i'm not sure anymore. I'm afraid it is prone to race conditions and things like that. Specifically the parts where i update the counters.
    Even though i'm using transactions inside my service, i'm not sure about transaction IsolationLevels.
    Is this ok ? Do i need to add something else ? Should i use @Version as well (beside transactions) ?
    I really need to maintain this denormalization (taskCount, projectTotalCost) and keep it consistent.



    @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);

    }

    }









    share|improve this question
























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      I am doing a project and i need to implement some denormalizations. Specifically i need to query Projects by taskCount and/or projectTotalCost.
      This is what i have so far.
      Seems to me like it works, but i'm not sure anymore. I'm afraid it is prone to race conditions and things like that. Specifically the parts where i update the counters.
      Even though i'm using transactions inside my service, i'm not sure about transaction IsolationLevels.
      Is this ok ? Do i need to add something else ? Should i use @Version as well (beside transactions) ?
      I really need to maintain this denormalization (taskCount, projectTotalCost) and keep it consistent.



      @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);

      }

      }









      share|improve this question













      I am doing a project and i need to implement some denormalizations. Specifically i need to query Projects by taskCount and/or projectTotalCost.
      This is what i have so far.
      Seems to me like it works, but i'm not sure anymore. I'm afraid it is prone to race conditions and things like that. Specifically the parts where i update the counters.
      Even though i'm using transactions inside my service, i'm not sure about transaction IsolationLevels.
      Is this ok ? Do i need to add something else ? Should i use @Version as well (beside transactions) ?
      I really need to maintain this denormalization (taskCount, projectTotalCost) and keep it consistent.



      @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);

      }

      }






      java sql spring jpa denormalization






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 5 at 1:59









      justatester

      11618




      11618





























          active

          oldest

          votes











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53147370%2fspring-jpa-denormalize-one-to-many%23new-answer', 'question_page');
          }
          );

          Post as a guest





































          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53147370%2fspring-jpa-denormalize-one-to-many%23new-answer', 'question_page');
          }
          );

          Post as a guest




















































































          這個網誌中的熱門文章

          Xamarin.form Move up view when keyboard appear

          Post-Redirect-Get with Spring WebFlux and Thymeleaf

          Anylogic : not able to use stopDelay()