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

视觉

如果必须保留对象的“复杂”结构,则@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表中分配了三个人记录作为人
  • !!! 该类别在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);
  • 该类别具有分配为经理的三个人记录(请参阅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 --------------------------------------------------------------------------
    
    // ...
    
}

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 --------------------------------------------------------------------------
    
    // ... 
}

Item.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 --------------------------------------------------------------------------
    
    // ...
}

财产人应包括分配给特定物品的人。属性类别应包含为多个项目分配的可选类别。类别的管理者将分配给其项目的人员属性。

非常简单直接。

相应的存储库接口和服务类

存储库接口扩展了JpaRepository接口。服务类包含用于创建,获取,更新和删除数据库中记录的方法。((我只是发布其中之一))

@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

的属性的ArrayList对象managers物体的cat2匝数成调用方法时一个PersistentBag对象save(S entity)JpaRepository或相当CrudRepository现在,此集合对象不仅包含Person对象的实体似乎该对象还表示实体Category之间的关系Person

另外要记住:Java对象变量指向内存堆中的对象。
因此,在这一行中Item item2 = new Item("Item 2", cat2.getManagers(), cat2);getManagers()方法将对内存中真实对象的引用移交给实体的构造函数,Item而不仅仅是值。
现在,当这个新实体必须存储在数据库中并且PersistentBag对象现在表示项目与其人员之间的关系时,它将像这样存储。这样关系将被移动。

解释情况3

该行cat3.setManagers(personList);只是用ArrayList对象替换PersistentBag对象。
因为列表未更改,所以调用了更新,但是没有关系的更新,并且该getManagers()方法未将对类别的PersistentBag的引用移交给内存,并且保持“不变”。

如果我们在更新之前更改List对象,则还将执行关系的更新:

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);

潜在的噩梦

这可能会有很多副作用。在这个简单的示例中,如果您知道这一点,则很容易调试。但是考虑一下散布在许多方法上的巨大代码。然后一种方法使用参数中的ArrayList进行更改或将其分配给其他实体,而无需了解原始实体或这是一个PersistentBag ...

一种解决方案

我要记住,不要仅使用方法的返回值getManagers()此外,我应该创建一个新的集合对象。

也许像这样:

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);

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

传递给另一个线程的 Spring Boot JPA 存储库不起作用

来自分类Dev

通过Spring Data JPA存储库和Spring Data JDBC存储库共享一个数据库事务

来自分类Dev

Hibernate Spring-@OneToMany-外键未存储在数据库中

来自分类Java

如何使用存储在数据库表中的属性配置Spring Bean

来自分类Dev

在Spring JPA存储库中读取未提交的数据

来自分类Dev

用户未存储在Spring数据库中

来自分类Dev

Spring数据JPA:另一个LazyInitializationException问题

来自分类Java

Spring Cloud Config Server-配置服务器以从一个存储库获取公共属性,而应用程序属性形成另一个存储库

来自分类Java

Spring Data JPA例外:数据库中已经有一个名为“ disease_view”的对象

来自分类Dev

Spring Batch从数据库中读取一次,写入另一个数据库并写入CSV

来自分类Java

Spring Boot-无法在数据库中创建一个实体

来自分类Java

在测试文件夹Spring中定义一个JPA存储库

来自分类Dev

在Spring Data JPA中使用CRUD存储库,由findById(long id)方法返回的日期为day-1,存储在数据库中的日期为Date

来自分类Dev

如何根据 Spring Data JPA 中另一个属性的值返回 Map?

来自分类Dev

如果移到Spring Boot应用程序的另一个软件包中,为什么找不到我的Spring Data存储库?

来自分类Dev

Spring数据继承,用于存储库重用

来自分类Java

REST API响应数据未使用Spring JPA保存在数据库中

来自分类Java

在Service类中创建一个方法以在Spring Boot中从存储库获取数据

来自分类Dev

Spring JPA不存储到数据库

来自分类Java

Spring Boot-Google OAuth2,将刷新令牌存储在数据库中

来自分类Java

Spring Batch-如何防止批次将事务存储在数据库中

来自分类Dev

我如何获取一个类对象的ID,在另一个类中并将其存储在数据库中-Django

来自分类Dev

使用spring-mvc和hibernate将文件名存储在数据库中,而不是将文件本身存储在数据库中

来自分类Java

Spring JPA:将实体对象保存在数据库中

来自分类Java

我可以有一个单一的JPA存储库接口,该接口使用Spring Data JPA处理多个实体类吗?

来自分类Java

一对一关系时使用Spring存储库获取数据

来自分类Dev

如何从另一个Spring Boot应用程序访问一个Spring Boot应用程序的内存h2数据库

来自分类Java

Spring Boot无法连接到部署在另一个kubernetes pod上的postgre数据库

来自分类Java

通用Spring数据JPA存储库findAll

Related 相关文章

  1. 1

    传递给另一个线程的 Spring Boot JPA 存储库不起作用

  2. 2

    通过Spring Data JPA存储库和Spring Data JDBC存储库共享一个数据库事务

  3. 3

    Hibernate Spring-@OneToMany-外键未存储在数据库中

  4. 4

    如何使用存储在数据库表中的属性配置Spring Bean

  5. 5

    在Spring JPA存储库中读取未提交的数据

  6. 6

    用户未存储在Spring数据库中

  7. 7

    Spring数据JPA:另一个LazyInitializationException问题

  8. 8

    Spring Cloud Config Server-配置服务器以从一个存储库获取公共属性,而应用程序属性形成另一个存储库

  9. 9

    Spring Data JPA例外:数据库中已经有一个名为“ disease_view”的对象

  10. 10

    Spring Batch从数据库中读取一次,写入另一个数据库并写入CSV

  11. 11

    Spring Boot-无法在数据库中创建一个实体

  12. 12

    在测试文件夹Spring中定义一个JPA存储库

  13. 13

    在Spring Data JPA中使用CRUD存储库,由findById(long id)方法返回的日期为day-1,存储在数据库中的日期为Date

  14. 14

    如何根据 Spring Data JPA 中另一个属性的值返回 Map?

  15. 15

    如果移到Spring Boot应用程序的另一个软件包中,为什么找不到我的Spring Data存储库?

  16. 16

    Spring数据继承,用于存储库重用

  17. 17

    REST API响应数据未使用Spring JPA保存在数据库中

  18. 18

    在Service类中创建一个方法以在Spring Boot中从存储库获取数据

  19. 19

    Spring JPA不存储到数据库

  20. 20

    Spring Boot-Google OAuth2,将刷新令牌存储在数据库中

  21. 21

    Spring Batch-如何防止批次将事务存储在数据库中

  22. 22

    我如何获取一个类对象的ID,在另一个类中并将其存储在数据库中-Django

  23. 23

    使用spring-mvc和hibernate将文件名存储在数据库中,而不是将文件本身存储在数据库中

  24. 24

    Spring JPA:将实体对象保存在数据库中

  25. 25

    我可以有一个单一的JPA存储库接口,该接口使用Spring Data JPA处理多个实体类吗?

  26. 26

    一对一关系时使用Spring存储库获取数据

  27. 27

    如何从另一个Spring Boot应用程序访问一个Spring Boot应用程序的内存h2数据库

  28. 28

    Spring Boot无法连接到部署在另一个kubernetes pod上的postgre数据库

  29. 29

    通用Spring数据JPA存储库findAll

热门标签

归档