Java Spring JPA:重用属性存储另一个对象时,数据库中未存储多个关系

Java Spring JPA:重用属性存储另一个对象时,数据库中未存储多个关系,java,spring-data-jpa,Java,Spring Data Jpa,如果必须持久化对象的“复杂”结构,那么@manytomy关联就有问题 我有一个Java应用程序使用SpringBoot(SpringBootStarterParent2.2.5.RELEASE)和JPA(SpringBootStarterDataJPA)。作为一个数据库,我尝试了MySQL和H2。效果是一样的 摘要1 我在创建类别时使用列表对象,在创建项目对象时使用相同的列表对象 Category cat1 = new Category("Cat 1", personList

如果必须持久化对象的“复杂”结构,那么@manytomy关联就有问题

我有一个Java应用程序使用SpringBoot(SpringBootStarterParent2.2.5.RELEASE)和JPA(SpringBootStarterDataJPA)。作为一个数据库,我尝试了MySQL和H2。效果是一样的

摘要1

我在创建类别时使用列表对象,在创建项目对象时使用相同的列表对象

Category cat1 = new Category("Cat 1", personList, null);
categoryService.create(cat1);
Item item1 = new Item("Item 1", personList, cat1);
itemService.create(item1);
Category cat2 = new Category("Cat 2", personList, null);
categoryService.create(cat2);
Item item2 = new Item("Item 2", cat2.getManagers(), cat2);
itemService.create(item2);

  • 类别记录在类别管理器表中有三个人被指定为经理
  • 项目记录中有三条人员记录被指定为项目人员表中的人员
摘要2

我在创建类别时使用列表对象,在创建项目对象时重用category.getManager列表

Category cat1 = new Category("Cat 1", personList, null);
categoryService.create(cat1);
Item item1 = new Item("Item 1", personList, cat1);
itemService.create(item1);
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

现在,使用与摘要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);
  • 该类别有三个人记录被指定为经理(请参见.category\u managers表)
  • 该项目有三个指定为人员的人员记录(参见项目\人员表)
问题

摘要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 --------------------------------------------------------------------------
    
    // ...
    
}
Category.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 --------------------------------------------------------------------------
    
    // ... 
}
三个测试用例

这里有一段代码来演示我在数据库中创建记录时面临的问题。 有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)
        
        
    }

}
@组件
公共类数据库初始值设定项实现CommandLineRunner{
@自动连线
类别服务类别服务;
@自动连线
项目服务项目服务;
@自动连线
人情服务人情服务;
@凌驾
公共无效运行(字符串…参数)引发异常{
//造人
personmax=personService.create(新人物(“max”、“Mad”);
personsally=personService.create(新人物(“sally”、“傻瓜”);
personbob=personService.create(新人物(“bob”、“Bad”);
List personList=Arrays.asList(max、sally、bob);
//案例1(本工程)
//这里我们将对类别和项目使用personList对象。
类别cat1=新类别(“类别1”,个人列表,空);
categoryService.create(cat1);
项目1=新项目(“项目1”,个人列表,类别1);
itemService.create(item1);
//=>类别记录中有三个人记录被分配为经理(请参见.category\u managers表)
//=>项目记录有三个人记录被分配为人员(请参见项目人员表)
//案例2(这不起作用)
//在这里,我们将使用personList对象来创建类别,并重用category.getManager列表来创建项目。
类别cat2=新类别(“类别2”,个人列表,空);
categoryService.create(cat2);
Item item2=新项(“Item 2”,cat2.getManagers(),cat2);
itemService.create(item2);
//=>该类别没有经理分配(请参见.category\u managers表)为什么??
//=>该项目有三条指定为人员的人员记录(请参见.item\u persons表)
//案例3(案例2的变通方法)
//在这里,我们将执行与案例2中相同的操作,但将对该类别的经理进行额外的分配
类别cat3=新类别(“类别3”,个人列表,空);
categoryService.create(cat3);
cat3.设置经理(个人列表);
类别服务更新(cat3);
Item item3=新项目(“Item 3”,cat3.getManagers(),cat3);
itemService.create(item3);
//=>该类别有三个人记录被指定为经理(请参见.category\u managers表)
//=>该项目有三条指定为人员的人员记录(请参见.item\u persons表)
}
}

我将直接进入案例2。它不起作用,因为它不在事务中。创建实体时:
新类别(“类别2”,个人列表,空)该实体已分离。然后调用事务方法
categoryService.create(cat2)并将其交给类别。在事务中,实体将被持久化,并且试图让管理者工作,但是一旦事务完成,数据库更新,我们将返回
run()
方法我们不再在该事务中。实体cat2是在该事务之外创建的,并且在事务完成后不会更新。如果您希望能够使用相同的方法访问
cat2.getManagers()
,我建议将
run()
方法设置为事务性的。

尝试解释案例2

调用
JpaRepository
crudepository
的方法
save(S entity)
时,对象
cat2
的属性
managers
的ArrayList对象将变为PersistentBag对象。现在,这个集合对象不仅仅包含
Person
对象的实体。它还表示实体
类别
个人
之间的关系

还要记住:Java对象变量指向内存堆中的对象。
因此,在
Item item2=new Item(“Item 2”,cat2.getManagers()行中,
Category cat3 = new Category("Cat 3", personList);
categoryService.create(cat3);
personList.add(personService.create(new Person("Mazy", "Lazy")));
cat3.setManagers(personList);
categoryService.update(cat3);
Item item3 = new Item("Item 3", cat3.getManagers(), cat3);
itemService.create(item3);
Category cat2 = new Category("Cat 2", personList);
categoryService.create(cat2);
List<Person> itemPersonList = new ArrayList<>();
cat2.getManagers().forEach(itemPersonList::add);
Item item2 = new Item("Item 2", itemPersonList, cat2);
itemService.create(item2);