title: detached entity issue when updating entity spring jpa
body: <p>I've looked at a bunch of answers to similar questions and I haven't found one that solves the issue for me. I feel completely lost right now.</p>
  <p>I'm developping a Spring Boot web app, using Spring Data JPA for persistence. I get the following error when I try to do an update to an existing entity:</p>
  <p>org.hibernate.PersistentObjectException: detached entity passed to persist: com.project.timefit.model.WeeklyRoutinePlan</p>
  <p>I'm trying to edit an entity WeeklyRoutinePlan using a crudRepository (the get, create and delete methods work fine).</p>
  <p>Here is the method in my service</p>
  <pre><code>@Override
  @Transactional
  public void editRoutinePlan(WeeklyRoutinePlan routinePlan, String username) {
      WeeklyRoutinePlan oldRoutinePlan = weeklyRoutinePlanRepository.findById(routinePlan.getId()).get();
      oldRoutinePlan.setWeekDay(routinePlan.getWeekDay());
      weeklyRoutinePlanRepository.save(oldRoutinePlan);
  }
  </code></pre>
  <p>I've simplified it for the sake of debugging, normally i would do a username verification and map the other fields too. I still get the error with this simplified version. And I even get it if I don't change the weekday.</p>
  <p>My entity itself looks like this</p>
  <pre><code>@Entity
  @Data
  @SuperBuilder
  @NoArgsConstructor
  public class WeeklyRoutinePlan{
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      @ManyToOne
      private Routine routine;
      private LocalTime startTime;
      private LocalTime endTime;
      private Integer weekDay;
      @ManyToOne
      private Program program;
  
      public void setRoutine(Routine routine) {
          if(routine == this.routine){
              return;
          }
          if(this.routine != null){
              this.routine.getWeeklyRoutinePlans().remove(this);
          }
          if(!routine.getWeeklyRoutinePlans().contains(this)){
              routine.getWeeklyRoutinePlans().add(this);
          }
          this.routine = routine;
      }
  
      public void setProgram(Program program) {
          if(program == this.program){
              return;
          }
          if(this.program != null){
              this.program.getWeeklyRoutines().remove(this);
          }
          if(!program.getWeeklyRoutines().contains(this)){
              program.getWeeklyRoutines().add(this);
          }
          this.program = program;
      }
  }
  </code></pre>
  <p>And here are the related entities (although I'm not sure that's relevant since I'm not even updating the relations)</p>
  <p>Routine</p>
  <pre><code>@Entity
  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  public class Routine {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      @ManyToOne
      private User user;
      private String name;
      private Integer numberOfCycles;
      private Color color;
      @ManyToMany(mappedBy = &quot;routines&quot;)
      private List&lt;Exercise&gt; exercises;
      @OneToMany(mappedBy = &quot;routine&quot;, fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
      private List&lt;WeeklyRoutinePlan&gt; weeklyRoutinePlans = new ArrayList&lt;&gt;();
      @OneToMany(mappedBy = &quot;routine&quot;, fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
      private List&lt;FrequencyRoutinePlan&gt; frequencyRoutinePlans = new ArrayList&lt;&gt;();
      @OneToMany(mappedBy = &quot;routine&quot;, fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
      private List&lt;IndividualRoutinePlan&gt; individualRoutinePlans = new ArrayList&lt;&gt;();
  
      public void remove(){
          exercises.forEach(exercise -&gt; exercise.getRoutines().remove(this));
          exercises = new ArrayList();
      }
      public void setExercises(List&lt;Exercise&gt; exercises){
          exercises.forEach(e -&gt; {
              if(e.getRoutines().stream().noneMatch(r -&gt; Objects.equals(r.getId(), this.getId())))
                  e.getRoutines().add(this);
          });
          this.exercises = exercises;
      }
  
      public void setWeeklyRoutinePlans(List&lt;WeeklyRoutinePlan&gt; weeklyRoutinePlans) {
          if(weeklyRoutinePlans == this.weeklyRoutinePlans){
              return;
          }
          this.weeklyRoutinePlans.forEach(r -&gt; {if(!weeklyRoutinePlans.contains(r))r.setRoutine(null);});
          weeklyRoutinePlans.forEach(r -&gt; {if(weeklyRoutinePlans.contains(r))r.setRoutine(this);});
          this.weeklyRoutinePlans = weeklyRoutinePlans;
      }
  
      public void setFrequencyRoutinePlans(List&lt;FrequencyRoutinePlan&gt; frequencyRoutinePlans) {
          if(frequencyRoutinePlans == this.frequencyRoutinePlans){
              return;
          }
          this.frequencyRoutinePlans.forEach(r -&gt; {if(!frequencyRoutinePlans.contains(r))r.setRoutine(null);});
          frequencyRoutinePlans.forEach(r -&gt; {if(frequencyRoutinePlans.contains(r))r.setRoutine(this);});
          this.frequencyRoutinePlans = frequencyRoutinePlans;
      }
  
      public void setIndividualRoutinePlans(List&lt;IndividualRoutinePlan&gt; individualRoutinePlans) {
          if(individualRoutinePlans == this.individualRoutinePlans){
              return;
          }
          this.individualRoutinePlans.forEach(r -&gt; {if(!individualRoutinePlans.contains(r))r.setRoutine(null);});
          individualRoutinePlans.forEach(r -&gt; {if(individualRoutinePlans.contains(r))r.setRoutine(this);});
          this.individualRoutinePlans = individualRoutinePlans;
      }
  
      @Override
      public String toString() {
          return &quot;Routine{&quot; +
                  &quot;id=&quot; + id +
                  &quot;, user=&quot; + user.getUsername() +
                  &quot;, name='&quot; + name + '\'' +
                  &quot;, numberOfCycles=&quot; + numberOfCycles +
                  &quot;, color=&quot; + color +
                  '}';
      }
  }
  </code></pre>
  <p>Program</p>
  <pre><code>@Entity
  @Data
  @AllArgsConstructor
  @NoArgsConstructor
  @SuperBuilder
  public class Program {
  
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      private String name;
      private ProgramSetting programSetting = ProgramSetting.WEEKLY;
      private Integer frequency = 1;
      @OneToMany(mappedBy = &quot;program&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
      private List&lt;WeeklyRoutinePlan&gt; weeklyRoutines = new ArrayList&lt;&gt;();
      @OneToMany(mappedBy = &quot;program&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
      private List&lt;FrequencyRoutinePlan&gt; frequencyRoutines = new ArrayList&lt;&gt;();
      @OneToMany(mappedBy = &quot;program&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
      private List&lt;IndividualRoutinePlan&gt; individualRoutines = new ArrayList&lt;&gt;();
      @ManyToOne
      private User user;
  
      public void setWeeklyRoutines(List&lt;WeeklyRoutinePlan&gt; weeklyRoutines) {
          if(weeklyRoutines == this.weeklyRoutines){
              return;
          }
          this.weeklyRoutines.forEach(r -&gt; {if(!weeklyRoutines.contains(r))r.setProgram(null);});
          weeklyRoutines.forEach(r -&gt; {if(weeklyRoutines.contains(r))r.setProgram(this);});
          this.weeklyRoutines = weeklyRoutines;
      }
  
      public void setFrequencyRoutines(List&lt;FrequencyRoutinePlan&gt; frequencyRoutines) {
          if(frequencyRoutines == this.frequencyRoutines){
              return;
          }
          this.frequencyRoutines.forEach(r -&gt; {if(!frequencyRoutines.contains(r))r.setProgram(null);});
          frequencyRoutines.forEach(r -&gt; {if(frequencyRoutines.contains(r))r.setProgram(this);});
          this.frequencyRoutines = frequencyRoutines;
      }
  
      public void setIndividualRoutines(List&lt;IndividualRoutinePlan&gt; individualRoutines) {
          if(individualRoutines == this.individualRoutines){
              return;
          }
          this.individualRoutines.forEach(r -&gt; {if(!individualRoutines.contains(r))r.setProgram(null);});
          individualRoutines.forEach(r -&gt; {if(individualRoutines.contains(r))r.setProgram(this);});
          this.individualRoutines = individualRoutines;
      }
  
      public void setUser(User user) {
          this.user = user;
      }
  
      @Override
      public String toString() {
          return &quot;Program{&quot; +
                  &quot;id=&quot; + id +
                  &quot;, name='&quot; + name + '\'' +
                  &quot;, programSetting=&quot; + programSetting +
                  &quot;, frequency=&quot; + frequency +
                  &quot;, user=&quot; + user.getUsername() +
                  '}';
      }
  }
  </code></pre>
  <p>I'm a little desperate at this point because my other entities work fine and I just don't get what I'm doing wrong. The method is transactional so the entity shouldn't be detached when I do the save.</p>
  <p>I get the same issue if I don't explicitly call save since that's done by the framework anyway.</p>
  <p>I've tried setting the fetch on the relations to LAZY, I've also tried EAGER, I've tried having the cascade on Program be only REMOVE, even though best practice is ALL but I'd read some people had issues with PERSIST.</p>
  <p>I've made sure my setters update the relations properly even though as I said I've simplified the update so that I'm not even using them.</p>
  <p>I'm at a loss</p>