如果必须保留对象的“复杂”结构,我会遇到 @ManyToMany 关联的问题。
我有一个使用 Spring Boot (spring-boot-starter-parent 2.2.5.RELEASE) 和 JPA (spring boot-starter-data-jpa) 的 Java 应用程序。作为数据库,我尝试了MySQL和H2。效果是一样的。
摘要1
我在创建类别时使用 List 对象,并在创建项目对象时使用相同的 List 对象。
Category cat1 = new Category("Cat 1", personList, null);
categoryService.create(cat1);
Item item1 = new Item("Item 1", personList, cat1);
itemService.create(item1);
- 该类别记录在category_managers 表中指定了三个人员记录
- 该项目记录在 item_persons 表中分配了三个人员记录
摘要2
我在创建类别时使用 List 对象,并在创建项目对象时重用category.getManager 列表。
Category cat2 = new Category("Cat 2", personList, null);
categoryService.create(cat2);
Item item2 = new Item("Item 2", cat2.getManagers(), cat2);
itemService.create(item2);
- 该项目在 item_persons 表中分配了 3 个人员记录
- >!!!该类别在category_managers 表中没有经理分配
摘要3
现在与摘要 2 中的过程相同,但进行了额外的分配和更新:
Category cat3 = new Category("Cat 3", personList, null);
categoryService.create(cat3);
cat3.setManagers(personList);
categoryService.update(cat3);
Item item3 = new Item("Item 3", cat3.getManagers(), cat3);
itemService.create(item3);
- 该类别有 3 名指定为经理的人员记录(请参阅category_managers 表)
- 该项目具有分配为人员的三个人员记录(请参阅 item_persons 表)
问题
为什么摘要 2 中的分配没有保留?
我没有考虑到什么吗?
我是否误解了幕后发生的事情?
这可能是一个特殊的用例,但是有一些主题或章节我必须再次阅读吗?
来自斯图加特的问候
<小时/>详细说明
模型结构
Person.java:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
// Constructors -------------------------------------------------------------------------------
public Person() {}
public Person(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
// Getters / Setters --------------------------------------------------------------------------
// ...
}
类别.java:
@Entity
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
private List<Person> managers;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name="category_id", updatable = false)
private List<Item> items;
// Constructors -------------------------------------------------------------------------------
public Category() {}
public Category(String name, List<Person> managers, List<Item> items) {
super();
this.name = name;
this.managers = managers;
this.items = items;
}
// Getters / Setters --------------------------------------------------------------------------
// ...
}
项目.java:
@Entity
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
private List<Person> persons;
@ManyToOne
private Category category;
// Constructors -------------------------------------------------------------------------------
public Item() {}
public Item(String name, List<Person> persons, Category category) {
super();
this.name = name;
this.persons = persons;
this.category = category;
}
// Getters / Setters --------------------------------------------------------------------------
// ...
}
属性(property)人员应包含分配给特定项目的人员。 属性类别应包含为多个项目分配的可选类别。类别的管理者将被分配给其项目的 person 属性。
非常简单直接。
对应的存储库接口(interface)和服务类
存储库接口(interface)扩展了 JpaRepository 接口(interface)。 服务类包含创建、获取、更新和删除数据库中记录的方法。 ((我只是发布其中之一))
@Service
public class PersonService {
@Autowired
PersonRepository personRepo;
public Person create(Person person) {
return personRepo.save(person);
}
public Person get(long id) {
return personRepo.getOne(id);
}
public void update(Person person) {
personRepo.save(person);
}
public void delete(long id) {
personRepo.deleteById(id);
}
}
三个测试用例
这里的代码演示了我在数据库中创建记录时遇到的问题。 共有3例。
@Component
public class DatabaseInitializer implements CommandLineRunner {
@Autowired
CategoryService categoryService;
@Autowired
ItemService itemService;
@Autowired
PersonService personService;
@Override
public void run(String... args) throws Exception {
// Create persons
Person max = personService.create(new Person("Max", "Mad"));
Person sally = personService.create(new Person("Sally", "Silly"));
Person bob = personService.create(new Person("Bob", "Bad"));
List<Person> personList = Arrays.asList(max, sally, bob);
// Case 1 (this works)
// Here we will use the personList object for both the category and the item.
Category cat1 = new Category("Cat 1", personList, null);
categoryService.create(cat1);
Item item1 = new Item("Item 1", personList, cat1);
itemService.create(item1);
// => The category record has three person records assigned as managers (see. category_managers table)
// => The item record has three person records assigned as persons (see. item_persons table)
// Case 2 (this doesn't work)
// Here we will use the personList object to create the category and reuse the category.getManager list to create the item.
Category cat2 = new Category("Cat 2", personList, null);
categoryService.create(cat2);
Item item2 = new Item("Item 2", cat2.getManagers(), cat2);
itemService.create(item2);
// => The category has no managers assignments (see. category_managers table) WHY??
// => The item has three person records assigned as persons (see. item_persons table)
// Case 3 (workaround of case 2)
// Here we will do the same as in case 2, but will do an extra assignment of the managers of the category
Category cat3 = new Category("Cat 3", personList, null);
categoryService.create(cat3);
cat3.setManagers(personList);
categoryService.update(cat3);
Item item3 = new Item("Item 3", cat3.getManagers(), cat3);
itemService.create(item3);
// => The category has three person records assigned as managers (see. category_managers table)
// => The item has three person records assigned as persons (see. item_persons table)
}
}
最佳答案
我将直接进入案例 2。它不起作用,因为它不在事务中。当您创建实体时:new Category("Cat 2", personList, null);
该实体将被分离。然后,您调用事务方法categoryService.create(cat2);
并将类别交给它。在事务中,实体被持久化,并且尝试让管理器工作但是,一旦事务完成并且数据库更新,我们返回到 run()
方法我们不再参与该交易。实体 cat2 是在该事务之外创建的,并且在事务完成后不会更新。如果您希望能够以相同的方法访问 cat2.getManagers()
,我建议将 run()
方法设置为事务性的。
关于java - Spring JPA : ManyToMany relationship not stored in database when reuse the property to store another object,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60778292/