Исключение одновременной модификации в методе saveAll () JPArepository (Spring DATA JPA + Hibernate) - PullRequest
0 голосов
/ 30 мая 2018

Я столкнулся с этой проблемой при сохранении «сотрудника» сущности, имеющего отношения oneToOne / oneToMany / ManyToMany.

Я использую Spring boot + Spring Data JPA с Hibernate и postgres как db.

пожалуйста, найдите ниже классы.

Класс сотрудника -

@Entity
@Table(name = "Employee")
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "emp_id")
private int emp_id;

@Column(name = "emp_name")
private String emp_name;

@Column(name = "d_o_b")
private Date date_of_birth;

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "community_employee_mapping", joinColumns = { @JoinColumn(name = "employee_id") }, inverseJoinColumns = { @JoinColumn(name = "community_id") })
private List<Community> listOfCommunities = new ArrayList<>();

@OneToOne(cascade = CascadeType.ALL)
private Department dept;

@OneToOne(cascade = CascadeType.ALL)
private Address address;

Класс DepartMent -

@Entity
@Table(name = "department")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int dept_id;

@Column(name = "dept_name")
private String departmentName;

@Column(name = "dept_desc")
private String departmentDesc;

@OneToMany(mappedBy = "dept", cascade = CascadeType.ALL)
private List<Employee> lemp = new ArrayList<>();

Класс сообщества -

@Entity
@Table(name = "community")
public class Community {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "com_id")
private int community_id;

@Column(name = "name")
private String community_name;

@ManyToMany(mappedBy = "listOfCommunities", cascade = CascadeType.ALL)
private List<Employee> listOfEmployees = new ArrayList<>();

@Column(name = "description")
private String description;

Адресный класс -

@Entity
@Table(name = "address")
public class Address {

@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int add_id;

@Column(name = "state")
private String state;

@Column(name = "city")
private String city;

@Column(name = "zip_code")
private String zip_code;

@Column(name = "country")
private String country;

@OneToOne(mappedBy = "address", cascade = CascadeType.ALL)
private Employee emp;

и ниже - мой метод весенней загрузки @test -

@Autowired
private EmployeeRepo erepo;

@Autowired
private CommunityRepo crepo;

@Autowired
private DeptRepo drepo;

@Autowired
private AddressRepo arepo;

@Test
public void contextLoads() {

    int max = 1 * 1000 * 100;

    Date date = new GregorianCalendar(1992, 06, 18).getTime();

    long start = System.currentTimeMillis();

    for (int index = 0; index < 50000; index++) {
        Employee emp0 = new Employee();
        Employee emp1 = new Employee();
        Employee emp2 = new Employee();

        emp0.setEmp_name(RandomStringUtils.randomAlphabetic(10) + " " + RandomStringUtils.randomAlphabetic(5));
        emp0.setDate_of_birth(DateUtils.addDays(date, MathUtil.nextInt(0, 5000)));

        emp1.setEmp_name(RandomStringUtils.randomAlphabetic(10) + " " + RandomStringUtils.randomAlphabetic(5));
        emp1.setDate_of_birth(DateUtils.addDays(date, MathUtil.nextInt(0, 5000)));

        emp2.setEmp_name(RandomStringUtils.randomAlphabetic(10) + " " + RandomStringUtils.randomAlphabetic(5));
        emp2.setDate_of_birth(DateUtils.addDays(date, MathUtil.nextInt(0, 5000)));

        Community comm0 = new Community();
        comm0.setCommunity_name(RandomStringUtils.randomAlphabetic(10) + " " + RandomStringUtils.randomAlphabetic(6));
        comm0.setDescription(RandomStringUtils.randomAlphabetic(20));

        Community comm1 = new Community();
        comm1.setCommunity_name(RandomStringUtils.randomAlphabetic(10) + " " + RandomStringUtils.randomAlphabetic(6));
        comm1.setDescription(RandomStringUtils.randomAlphabetic(20));

        List<Employee> lemp = new ArrayList<>();
        lemp.add(emp0);
        lemp.add(emp1);
        comm0.setListOfEmployees(lemp);

        List<Community> lcomm = new ArrayList<>();
        lcomm.add(comm0);

        emp0.setListOfCommunities(lcomm);
        emp1.setListOfCommunities(lcomm);

        List<Employee> lemp2 = new ArrayList<>();
        lemp2.add(emp2);
        comm1.setListOfEmployees(lemp2);

        List<Community> lcomm2 = new ArrayList<>();
        lcomm2.add(comm1);

        emp2.setListOfCommunities(lcomm2);

        Department dept0 = new Department();
        dept0.setDepartmentDesc(RandomStringUtils.randomAlphabetic(25));
        dept0.setDepartmentName(RandomStringUtils.randomAlphabetic(5));

        Department dept1 = new Department();
        dept1.setDepartmentDesc(RandomStringUtils.randomAlphabetic(25));
        dept1.setDepartmentName(RandomStringUtils.randomAlphabetic(5));

        List<Employee> lempp = new ArrayList<>();
        lempp.add(emp0);
        lempp.add(emp1);
        dept0.setLemp(lemp);

        emp0.setDept(dept0);
        emp1.setDept(dept1);

        List<Employee> lempp2 = new ArrayList<>();
        lempp2.add(emp2);
        dept1.setLemp(lempp2);

        emp2.setDept(dept1);

        Address add0 = new Address(RandomStringUtils.randomAlphabetic(7), RandomStringUtils.randomAlphabetic(5), String.valueOf(MathUtil.nextInt(999999, 9999999)),
                RandomStringUtils.randomAlphabetic(5), emp0);
        Address add1 = new Address(RandomStringUtils.randomAlphabetic(7), RandomStringUtils.randomAlphabetic(5), String.valueOf(MathUtil.nextInt(999999, 9999999)),
                RandomStringUtils.randomAlphabetic(5), emp1);
        Address add2 = new Address(RandomStringUtils.randomAlphabetic(7), RandomStringUtils.randomAlphabetic(5), String.valueOf(MathUtil.nextInt(999999, 9999999)),
                RandomStringUtils.randomAlphabetic(5), emp2);

        emp0.setAddress(add0);
        emp1.setAddress(add1);
        emp2.setAddress(add2);

        // erepo.save(emp0);
        // erepo.save(emp1);
        // erepo.save(emp2);

        List<Employee> saveEmp = new ArrayList<>();
        saveEmp.add(emp0);
        saveEmp.add(emp1);
        saveEmp.add(emp2);

        erepo.saveAll(saveEmp);

    }

    long end = System.currentTimeMillis();

    long elapsedTime = end - start;
    System.out.println("elapsed : " + (elapsedTime) + " mili sec.........");

    System.out.println("done writing to db ======================================================================");

}

Я использую CascadeType.ALL во всех отношениях, поэтому сохраняю только родительскийлицо, т.е. сотрудник.

Весь код прекрасно работает, когда я делаю ниже.Проблема этого подхода в том, что он медленный, так как мы открываем три сессии сзади и закрываем, в то время как у нас есть метод saveAll (итерируемые аргументы), который может сделать это за одну сессию.

// erepo.save(emp0);
// erepo.save(emp1);
// erepo.save(emp2);

без записи

erepo.saveAll(saveEmp);

Однако, когда я пишу saveAll из erepo, он создает arrayList всех элементов, которые мы передаем в качестве итеративного аргумента.Ниже приведен фрагмент кода из класса SimpleJPArepository платформы Spring.

@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {

    Assert.notNull(entities, "The given Iterable of entities not be null!");

    List<S> result = new ArrayList<S>();

    for (S entity : entities) {
        result.add(save(entity));
    }

    return result;
}

Я хочу подтвердить здесь две вещи ...

1) Поскольку я не присоединяю какой-либо идентификатор к какой-либо сущности ипозволяя генерировать его во время выполнения, используя @GeneratedValue.Сохраняя сущность, hibernate пытается присоединить идентификатор к сущности и, таким образом, изменяет его состояние и вызывает исключение ConcurrentModificationException.

2) Я могу заставить его работать, используя CopyOnWriteArrayList, но это снова будет занимать пространство в куче, так как будет клонировать весь граф объектов, и, следовательно, производительность будет отклоняться наверняка.

Можно ли каким-то образом сохранить saveAll () без какого-либо снижения производительности.

Пожалуйста, исправьте меня, если я где-то ошибаюсь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...