JPArepository的saveAll()方法中的并发修改异常(Spring DATA JPA + Hibernate)

时间:2018-05-30 11:58:23

标签: hibernate spring-boot jpa spring-data-jpa

我在保存具有oneToOne / oneToMany / ManyToMany类型关系的实体“employee”时遇到此问题。

我使用Spring引导+ 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,所以只保留父实体,即Employee。

当我在下面做的时候,整个代码完全正常。这种方法的问题是它很慢,因为我们在后面打开三个会话并关闭,而我们有saveAll(可迭代的args)方法,可以在单个会话中完成。

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

不写

erepo.saveAll(saveEmp);

然而,当我从erepo写saveAll时,它会创建一个我们将其作为可迭代参数传递的所有项的arrayList。下面是Spring框架的SimpleJPArepository类的代码片段。

@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)因为我没有将任何ID附加到任何实体并让它在运行时使用@GeneratedValue生成。虽然持久化实体hibernate试图将id附加到实体,从而改变其状态并导致ConcurrentModificationException。

2)我可以通过使用CopyOnWriteArrayList使其工作,但这将再次消耗堆中的空间,因为它将克隆整个对象图,因此性能肯定会有所偏差。

有没有办法可以坚持使用saveAll()而不会有任何性能损失。

如果我在任何地方都错了,请纠正我。

0 个答案:

没有答案