2017-10-02 13 views
2

removeRecipe 요리 책에 Cookbook에서 Recipe을 제거합니다. 참조를 null로 설정하고 콜렉션에서 엔티티를 제거한 후에 엔티티는 삭제되지 않습니다.봄 데이터 저장하지 않음

@Entity 
public class Cookbook implements Identifiable<Cookbook> { 
    private static final Logger LOG = LoggerFactory.getLogger(Cookbook.class); 
    private Long id; 
    private String title; 
    private List<CookbookRecipe> cookbookRecipes = new ArrayList<>(); 

    public Cookbook() {} 

    public Cookbook(String title) { 
     this.title = title; 
    } 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    @Basic 
    @Column(name = "title") 
    public String getTitle() { 
     return title; 
    } 
    public void setTitle(String title) { 
     this.title = title; 
    } 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "cookbook", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, orphanRemoval = true) 
    public List<CookbookRecipe> getCookbookRecipes() { 
     return cookbookRecipes; 
    } 

    /** 
    * The setter is called by hibernate. 
    * @param cookbookRecipes maybe null, maybe the collection is not even ready for read access. 
    *     Don't do anything with the collection here! 
    */ 
    public void setCookbookRecipes(List<CookbookRecipe> cookbookRecipes) { 
     this.cookbookRecipes = cookbookRecipes; 
    } 

    /** 
    * Returns a List that must remain unchanged. 
    */ 
    @Transient 
    public List<Recipe> getRecipes() { 
     return Collections.unmodifiableList(getCookbookRecipes().stream().map(CookbookRecipe::getRecipe).collect(Collectors.toList())); 
    } 

    public void addRecipe(Recipe recipe, String createdBy, Date createdDate) { 
     final CookbookRecipe cookbookRecipe = new CookbookRecipe(this, recipe); 
     cookbookRecipe.setCreatedBy(createdBy); 
     cookbookRecipe.setCreatedDate(createdDate); 
     if(!cookbookRecipes.contains(cookbookRecipe) && !recipe.getCookbookRecipes().contains(cookbookRecipe)) { 
      if(!cookbookRecipes.add(cookbookRecipe)) { 
       LOG.error("Failed to add cookbookRecipe " + cookbookRecipe + " to collection cookbookRecipes " + cookbookRecipes); 
      } 
      if(!recipe.getCookbookRecipes().add(cookbookRecipe)) { 
       LOG.error("Failed to add cookbookRecipe " + cookbookRecipe + " to collection recipe.getCookbookRecipes " + recipe.getCookbookRecipes()); 
      } 
     } 
    } 

    public void removeRecipe(Recipe recipe) { 

     for (Iterator<CookbookRecipe> iterator = cookbookRecipes.iterator(); 
      iterator.hasNext();) { 
      CookbookRecipe cookbookRecipe = iterator.next(); 

      if (cookbookRecipe.getCookbook().equals(this) && 
        cookbookRecipe.getRecipe().equals(recipe)) { 
       iterator.remove(); 
       recipe.getCookbookRecipes().remove(cookbookRecipe); 
       cookbookRecipe.setCookbook(null); 
       cookbookRecipe.setRecipe(null); 
      } 
     } 
    } 

    @Override 
    public String toString() { 
     return title; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 
     Cookbook that = (Cookbook) o; 
     return getId() != null && Objects.equals(getId(), that.getId()); 
    } 

    @Override 
    public int hashCode() { 
     return 31; 
    } 

    @Override 
    public boolean equalsByBusinessKey(Cookbook other) { 
     if (this == other) return true; 
     if (other == null || getClass() != other.getClass()) return false; 
     return Objects.equals(getTitle(), other.getTitle()); 
    } 
} 

@Entity 
@Table(name = "cookbook_recipe") 
public class CookbookRecipe implements Serializable { 
    @EmbeddedId 
    private CookbookRecipePk pk; 

    @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) 
    @MapsId("cookbookId") 
    private Cookbook cookbook; 

    @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) 
    @MapsId("recipeId") 
    private Recipe recipe; 

    private Date createdDate; 
    private String createdBy; 

    public CookbookRecipe() { 
    } 

    public CookbookRecipe(Cookbook cookbook, Recipe recipe) { 
     this.cookbook = cookbook; 
     this.recipe = recipe; 
     this.pk = new CookbookRecipePk(cookbook.getId(), recipe.getId()); 
    } 

    public CookbookRecipePk getPk() { 
     return pk; 
    } 

    public void setPk(CookbookRecipePk pk) { 
     this.pk = pk; 
    } 

    @Transient 
    public Cookbook getCookbook() { 
     return cookbook; 
    } 

    public void setCookbook(Cookbook cookbook) { 
     this.cookbook = cookbook; 
    } 

    @Transient 
    public Recipe getRecipe() { 
     return recipe; 
    } 

    public void setRecipe(Recipe recipe) { 
     this.recipe = recipe; 
    } 

    @Temporal(TemporalType.DATE) 
    @Column(name = "CREATED_DATE", nullable = false, length = 10) 
    public Date getCreatedDate() { 
     return this.createdDate; 
    } 

    public void setCreatedDate(Date createdDate) { 
     this.createdDate = createdDate; 
    } 

    @Column(name = "CREATED_BY", nullable = false, length = 10) 
    public String getCreatedBy() { 
     return this.createdBy; 
    } 

    public void setCreatedBy(String createdBy) { 
     this.createdBy = createdBy; 
    } 

    public boolean equals(Object o) { 
     if (this == o) 
      return true; 
     if (o == null || getClass() != o.getClass()) 
      return false; 

     CookbookRecipe that = (CookbookRecipe) o; 

     return Objects.equals(getPk(), that.getPk()); 
    } 

    public int hashCode() { 
     return 31; 
    } 

    @Override 
    public String toString() { 
     final StringBuilder sb = new StringBuilder("CookbookRecipe{"); 
     sb.append("pk=") 
      .append(pk); 
     sb.append('}'); 
     return sb.toString(); 
    } 
} 

@Embeddable 
public class CookbookRecipePk implements java.io.Serializable { 

    @Column(name = "cookbook_id") 
    private Long cookbookId; 

    @Column(name = "recipe_id") 
    private Long recipeId; 

    public CookbookRecipePk() {} 

    public CookbookRecipePk(Long cookbookId, Long recipeId) { 
     this.cookbookId = cookbookId; 
     this.recipeId = recipeId; 
    } 

    public Long getCookbookId() { 
     return cookbookId; 
    } 

    public void setCookbookId(Long cookbookId) { 
     this.cookbookId = cookbookId; 
    } 

    public Long getRecipeId() { 
     return recipeId; 
    } 

    public void setRecipeId(Long recipeId) { 
     this.recipeId = recipeId; 
    } 


    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 

     CookbookRecipePk that = (CookbookRecipePk) o; 

     return null != cookbookId && null != recipeId && 
       Objects.equals(cookbookId, that.cookbookId) && 
       Objects.equals(recipeId, that.recipeId); 
    } 

    public int hashCode() { 
     return 31; 
    } 

    @Override 
    public String toString() { 
     final StringBuilder sb = new StringBuilder("CookbookRecipePk{"); 
     sb.append("cookbookId=") 
      .append(cookbookId); 
     sb.append(", recipeId=") 
      .append(recipeId); 
     sb.append('}'); 
     return sb.toString(); 
    } 
} 

@Entity 
public class Recipe implements Serializable, Identifiable<Recipe> { 
    private Long id; 
    private String title; 
    private Category category; 
    private List<CookbookRecipe> cookbookRecipes = new ArrayList<>(); 

    public Recipe(String title) { 
     this.title = title; 
    } 

    public Recipe() {} 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    @Basic 
    @Column(name = "title") 
    public String getTitle() { 
     return title; 
    } 
    public void setTitle(String title) { 
     this.title = title; 
    } 

    @ManyToOne(cascade = CascadeType.ALL) 
    @JoinColumn(name = "category_id", referencedColumnName = "id") 
    public Category getCategory() { 
     return category; 
    } 

    public void setCategory(Category category) { 
     this.category = category; 
    } 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "recipe", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) 
    public List<CookbookRecipe> getCookbookRecipes() { 
     return cookbookRecipes; 
    } 

    public void setCookbookRecipes(List<CookbookRecipe> cookbookRecipes) { 
     this.cookbookRecipes = cookbookRecipes; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 
     Recipe that = (Recipe) o; 
     return getId() != null && Objects.equals(getId(), that.getId()); 
    } 

    @Override 
    public int hashCode() { 
     return 31; 
    } 

    @Override 
    public String toString() { 
     final StringBuilder sb = new StringBuilder("Recipe{"); 
     sb.append("id=") 
      .append(id); 
     sb.append(", title='") 
      .append(title) 
      .append('\''); 
     sb.append(", cookbookRecipes=") 
      .append(cookbookRecipes); 
     sb.append('}'); 
     return sb.toString(); 
    } 

    @Override 
    public boolean equalsByBusinessKey(Recipe other) { 
     if (this == other) return true; 
     if (other == null || getClass() != other.getClass()) return false; 
     return Objects.equals(getTitle(), other.getTitle()); 
    } 
} 

테스트

@RunWith(SpringRunner.class) 
@DataJpaTest 
public class CookbookRepositoryIntegrationTest { 

    @Autowired 
    RecipeRepository recipeRepository; 
    @Autowired 
    CookbookRepository cookbookRepository; 
    @Autowired 
    CookbookRecipeRepository cookbookRecipeRepository; 

    @Test 
    public void WhenAddingSameAssociationAgain_ThenNoException() { 
     Recipe recipe = new Recipe(); 
     recipe.setTitle("A Recipe"); 
     recipe = recipeRepository.save(recipe); 

     Cookbook cookbook = new Cookbook(); 
     cookbook.setTitle("A Cookbook"); 
     cookbook = cookbookRepository.save(cookbook); 

     cookbook.addRecipe(recipe, "integrationtest", new Date()); 
     cookbook = cookbookRepository.save(cookbook); 

     cookbook.removeRecipe(recipe); 
     cookbook = cookbookRepository.save(cookbook); 
     assertThat(cookbookRecipeRepository.findAll().size(), is(0)); 
    } 

} 

어설 실패. 나는 왜 그런지 이해하지 못한다.

java.lang.AssertionError: 
Expected: is <0> 
    but: was <1> 

orphanRemoval이 true로 설정되어 있기 때문에 JPA에서 DELETE 문을 생성 할 것으로 예상합니다. 대신 CookbookRecipe.recipeId 및 CookbookRecipe.cookbookId는 데이터베이스에서 null로 설정되지만 제거되지는 않습니다.

+0

'RecipeRepository.delete (Recipe)'와 같은 것을 호출하여'Recipe' 엔티티를 삭제해야합니다. 'null'에 대한 참조 설정이 충분하지 않습니다. –

+0

레시피를 삭제하고 싶지 않습니다. 요리 책에서 요리법을 삭제하고 싶습니다. 따라서 협회를 제거해야합니다. 그러나 JPA는 orphanRemoval이 true 일 때라도 필요한 DELETE 문을 작성하지 않습니다. 대신 값은 null로 설정됩니다. 이는 충분하지만 충분하지 않습니다. – kfaria

+0

나는 답을 알지 못한다. 그러나 무엇이 대체 된 equalsByBusinessKey (...)가 무엇인지 알기에 궁금하다. – FakirTrappedInCode

답변

0

'CookbookRecipe'엔티티가 매핑 된 @oneToMany가 'cookbook'에서 2 가지 방법으로, 'recipe'에서이 방식으로 제거되었으므로 Entity가 제거되지 않는다고 생각합니다. 스프링 데이터가 2에서 명령을 받아들이 기 때문에 요리 책에서 요리법을 삭제할 수 없습니다. 엔티티, 당신은 조리법에서 두 번째 conf의 oneTOMany를 취소해야하며 잘 작동합니다 또한 저장소에 @ 쿼리를 사용할 수 있습니다 (사용자 정의 쿼리를 작성하고 잘 작동합니다), 나는 1 년 전에이 문제에 직면, 희망 유용합니다