java - Spring JPA : ManyToMany relationship not stored in database when reuse the property to store another object

标签 java spring-data-jpa

如果必须保留对象的“复杂”结构,我会遇到 @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/

相关文章:

java - 如何从Android应用程序代号one中删除工具栏(使其全屏显示)

java - Java 中的 Monitor 和 Synchronized 关键字

spring - ReactiveCrudRepository 在 spring 中使用 Hibernate

google-app-engine - 谷歌应用引擎 JRE 类 "Black List"

java - 以编程方式更改复选框颜色

Java - 如何显示图像?

java - ManyToMany 的 JPA Criteria 规范

java - 是否可以替换或修改投影或匿名类中的值?

java - 无法在 xd-singlenode 上使用 spring-data-jpa 部署 spring-xd 处理器模块

spring - 阴影包试图在 Java 7 中查找 java.time.LocalTime