为什么我即使使用Spring Data JPA的CascadeType.PERSIST,也需要同步双向关系?

huangapple go评论77阅读模式
英文:

Why do I have to sync a bidirectional relationship even with CascadeType.PERSIST using Spring Data JPA

问题

以下是演示问题的示例代码:

餐饮实体:

@Entity
public class Meal {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @OneToMany(mappedBy = "meal", cascade = CascadeType.PERSIST)
  private Collection<Food> foods;

  public Meal() {
    foods = new HashSet<>();
  }

  public Collection<Food> getFoods() {
    return foods;
  }

  public void addFood(Food food) {
    foods.add(food);
    // without this the `meal_id` column is null
    food.setMeal(this);
  }
}

食物实体:

@Entity
public class Food {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @ManyToOne
  private Meal meal;

  public long getId() {
    return id;
  }

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

  public Meal getMeal() {
    return meal;
  }

  public void setMeal(Meal meal) {
    this.meal = meal;
  }
}

以下是创建并保存实体的代码:

Meal meal = new Meal();

for (int i = 0; i < 10; i++) {
  meal.addFood(new Food());
}

mealRepository.save(meal);

由于存在 CascadeType.PERSIST,餐饮和食物实体都会持久化,但如果不显式设置食物实体的 meal 字段为对应的餐饮实体,meal_id 列会保持为空。这不是我预期的行为,我想知道为什么不能自动执行这个操作。

英文:

Here is an example code demonstrating the issue:

The Meal Entity:

@Entity
public class Meal {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @OneToMany(mappedBy = &quot;meal&quot;, cascade = CascadeType.PERSIST)
  private Collection&lt;Food&gt; foods;

  public Meal() {
    foods = new HashSet&lt;&gt;();
  }

  public Collection&lt;Food&gt; getFoods() {
    return foods;
  }

  public void addFood(Food food) {
    foods.add(food);
    // without this the `meal_id` column is null
    food.setMeal(this);
  }
}

The Food Entity:

@Entity
public class Food {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @ManyToOne
  private Meal meal;

  public long getId() {
    return id;
  }

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

  public Meal getMeal() {
    return meal;
  }

  public void setMeal(Meal meal) {
    this.meal = meal;
  }
}

And here is the code that creates and saves the entities:

Meal meal = new Meal();

for (int i = 0; i &lt; 10; i++) {
  meal.addFood(new Food());
}

mealRepository.save(meal);

Both the Meal and the Food entities are persisted thanks to the CascadeType.PERSIST, but the meal_id column stays null if I don't explicitly set the meal field to the Meal entity.

This is not the behavior I'd expect, and I'm wondering why isn't this automatically done for me.

答案1

得分: 4

@Entity
public class Meal {

  @OneToMany(mappedBy = "meal", cascade = CascadeType.PERSIST)
  private Collection<Food> foods;

}

这里的mappedBy意味着Food类中的meal字段的值被用来提供连接FoodMeal之间相应数据库列(即Food表中的meal_id列)的值。因此,如果你不设置它,它将始终保持为空。

另一方面,没有mappedBy意味着Meal中的foods字段的值被用来提供meal_id列的值。手动同步它们的原因只是为了拥有一致的面向对象编程模型。

CascadeType.PERSIST与设置实体的值无关。它不会自动更新实体的值。它帮助的是自动在相关实体上调用一些entityManager方法。如果没有它,你需要调用以下代码来保存所有的FoodMeal

entityManager.persist(meal);
entityManager.persist(food1);
entityManager.persist(food2);
......
entityManager.persist(foodN);

有了它,你只需要调用:

entityManager.persist(meal);

剩下的entityManager.persist(foodN)将会被“级联”自动调用。

因此,在Spring Data JPA中,不是调用:

mealRepository.save(meal);
foodRepository.persist(food1);
foodRepository.persist(food2);
......
foodRepository.persist(foodN);

而是只需要调用:

mealRepository.save(meal);
英文:
@Entity
public class Meal {


  @OneToMany(mappedBy = &quot;meal&quot;, cascade = CascadeType.PERSIST)
  private Collection&lt;Food&gt; foods;

} 

The mappedBy here means that the value of meal field in Food is used to provide the value for the corresponding database column that link between Food and Meal (i.e. meal_id column in Food table). So if you do not set it, it will always remain NULL.

On the other hand, without mappedBy means that the value of the foods field in Meal is used to provide the value for meal_id column. The reason of manually syncing them is just to have a consistent OOP model to work with.

CascadeType.PERSIST is nothing to do with setting the value of the entity. It will never update the value of the entities for you. What it helps is to automatically calling some entityManager methods on the related entities. Without it, you have to call the following to save all Food and Meal:

entityManager.persist(meal);
entityManager.persist(food1);
entityManager.persist(food2);
......
entityManager.persist(foodN);

With it , you just need to call

entityManager.persist(meal);

and the rest of entityManager.persist(foodN) will be 'cascaded' to call automatically.

So in term of spring-data-jpa , instead of calling

mealRepository.save(meal);
foodRepository.persist(food1);
foodRepository.persist(food2);
......
foodRepository.persist(foodN);

You just need to call

mealRepository.save(meal);

huangapple
  • 本文由 发表于 2020年5月30日 03:28:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/62093369.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定