首页 > 解决方案 > 在Java中,在持久化之前解析嵌套对象时获得concurrentModification

问题描述

在具有 Spring 数据 jpa 的 Spring Boot 应用程序中,我得到一个表示数据库中实体的对象,具有多个级别的关联父级 -> 子级与 arrayLists。出于数据库完整性目的,我必须对某些属性进行一些检查,如果结果正常,我需要映射关联。但是如果没有 ConcurrentModificationException,我就无法做到这一点,因为我在迭代时正在修改对象。

这是我得到的对象:

{
  "name": "menu test",
  "description": "description test for menu",
  "price": 15,
  "menuCourses": [
    {
      "category": "Entrée",
      "productsInCourse": [
        {
          "product": {
            "id": 1
          },
          "extraCost": 0
        }
      ]
    },
    {
      "category": "Plat",
      "productsInCourse": [
        {
          "product": {
            "id": 4
          },
          "extraCost": 2
        }
      ]
    }
  ]
}

这是我所关注的实体(我省略了 getter 设置器和构造器):


@Entity
public class Menu {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    private Double price;
    @OneToMany(
            mappedBy = "menu",
            cascade = CascadeType.ALL,
            orphanRemoval = true)
    private List<MenuCourse> menuCourses = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    private Restaurant restaurant;

    public void addCourse(MenuCourse course) {
        this.menuCourses.add(course);
        course.setMenu(this);
    }

@Entity
public class MenuCourse {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @OneToMany(
            mappedBy = "menuCourse",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private List<ProductInCourse> productsInCourse = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    private Category category;
    @ManyToOne(fetch = FetchType.LAZY)
    private Menu menu;

    public void addProductInMenu(ProductInCourse productInCourse) {
        this.productsInCourse.add(productInCourse);
        productInCourse.setMenuCourse(this);
    }

@Entity
public class ProductInCourse {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Double extraCost;

    @ManyToOne()
    private Product product;
    @ManyToOne(fetch = FetchType.LAZY)
    private MenuCourse menuCourse;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String imageUrl;
    @OneToMany(
            mappedBy = "product",
            cascade = CascadeType.ALL,
            orphanRemoval = true,
            fetch = FetchType.LAZY
    )
    private List<ProductInCourse> productsInMenu = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    private Category category;
    @ManyToOne(fetch = FetchType.LAZY)
    private Restaurant restaurant;

我的业务层在持久化之前检查对象,

 public Menu saveMenu(Long restaurantId, Menu menu) {
        if(this.restaurantRepository.findById(restaurantId).isPresent()) {
            menu.setRestaurant(Restaurant.builder().id(restaurantId).build());

            menu.getMenuCourses().forEach(menuCourse -> {
                if (this.categoryRepository.findCategoryByName(menuCourse.getCategory().getName()).isPresent()) {
                    menu.addCourse(menuCourse); <-- List modification is here

                    menuCourse.getProductsInCourse().forEach(productInCourse -> {
                        if (this.productRepository.findProductByIdAndRestaurantId(
                                productInCourse.getProduct().getId(),
                                restaurantId)
                                .isPresent()) {
                            menuCourse.addProductInMenu(productInCourse); <-- List modification is here
                        } else {
                            throw new UnknownResourceException("Unknown Product '" + productInCourse.getProduct().getId() + "'");
                        }
                    });
                } else {
                    throw new UnknownResourceException("Unknown Category '" + menuCourse.getCategory().getName() + "'");
                }
            });
            menuRepository.save(menu);
        } else {
            throw new UnknownResourceException("Unknown Restaurant '" + restaurantId + "'");
        }
        return menu;

我的问题是我有嵌套循环,我不知道如何制定一个优雅的解决方案来进行这些检查。我尝试使用流,但我没有设法做任何接近工作的事情。感谢帮助 !

标签: java

解决方案


我发现解析嵌套列表而没有并发问题的唯一解决方案是使用索引而不是迭代器。我对解决方案不是很满意,我很确定还有更优雅的事情要做,但不幸的是我无法弄清楚。

    @Override
    public Menu saveMenu(Long restaurantId, Menu menu) {
        Restaurant restaurant = this.restaurantRepository
                .findById(restaurantId)
                .orElseThrow(() -> 
                        new UnknownResourceException("Unknown Restaurant '" + restaurantId + "'")
                );
        log.info("Restaurant id '{}' found", restaurant.getId());
        menu.setRestaurant(restaurant);

        for (int i = 0; i < menu.getMenuCourses().size(); i++) {
            MenuCourse menuCourse = menu.getMenuCourses().get(i);
            String categoryName = menuCourse.getCategory().getName();
            Category category = this.categoryRepository
                    .findCategoryByName(categoryName)
                    .orElseThrow(() -> 
                            new UnknownResourceException("Unknown Category '" + categoryName + "'")
                    );
            log.info("Category name '{}' found", category);
            menuCourse.setMenu(menu);
            menuCourse.setCategory(category);
            menu.getMenuCourses().set(i, menuCourse);

            for (int j = 0; j < menuCourse.getProductsInCourse().size(); j++) {
                ProductInCourse productInCourse = menuCourse.getProductsInCourse().get(j);
                Product product = this.productRepository
                        .findProductByIdAndRestaurantId(
                                productInCourse.getProduct().getId(),
                                restaurantId)
                        .orElseThrow(() -> 
                                new UnknownResourceException("Unknown Product '" + productInCourse.getProduct().getId() + "'")
                        );
                log.info("Product id '{}' for restaurant '{}' found", product.getId(), restaurantId);
                productInCourse.setProduct(product);
                productInCourse.setMenuCourse(menuCourse);
                menuCourse.getProductsInCourse().set(j, productInCourse);
            }
        }
        return menuRepository.save(menu);
    }

我对更好的解决方案非常感兴趣!


推荐阅读