如何正确保存关联的实体?

时间:2018-10-15 11:20:15

标签: java spring-mvc model-view-controller spring-data-jpa

假设我们有以下三个域模型实体:CompanyDepartamentEmployee

@Getter @Setter @NoArgsConstrutor
public class Employee {
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id", nullable = false, insertable = false, updatable = false)
    private Department department;

    @JoinColumn(name = "department_id", nullable = false)
    private int department_id;
}

@Getter @Setter @NoArgsConstrutor
public class Department {
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id", nullable = false, insertable = false, updatable = false)
    private Company company;

    @JoinColumn(name = "company_id", nullable = false)
    private int company_id;

    @OneToMany(mappedBy = "department")
    private List<Employee> employees;
}

@Getter @Setter @NoArgsConstrutor
private class Company {
    private String name;

    @OneToMany(mappedBy = "company")
    private List<Department> departments;
}

对于每个实体,我们都有Repositories,它们扩展了JpaRepositoryServicesControllers。在每个Service中,我们分别@Autowire Repository,在每个实体Controller中,我们从实体Service调用方法。

我的问题是以下:我无法保存整个Company,因为Departments需要一个Company ID,而Employees Deparment的ID。因此,首先,在我的CompanyService中保存,然后清除departments列表,执行一个saveAndFlush,该ID为我的company分配ID。我将接收到的ID分配给先前保存的company_id列表的每个实体中的每个departments,然后将列表附加回company并执行另一个saveAndFlushemployee列表再花一次。

@RestController
public class CompanyController {
    @Autowire
    private CompanyService companyService;

    @PostMapping("/companies")
    public Company createCompany(@RequestBody Company newCompany) {
        return companyService.createCompany(newCompany);
    }
}

@Service
public class CompanyService {
    @Autowire
    private CompanyRepository companyRepository;

    public Company createCompany(Company company) {
        List<Department> departments = new ArrayList<>(company.getDepartments());
        company.getDepartments().clear();

        companyRepository.saveAndFlush(company);

        int company_id = company.getId();

        departments.forEach (department ->
            department.setCompany_id(company_id);
        );

        //here I save a copy of the previously saved departments, because I still need the employees
        company.getDepartments().addAll(departments.stream().map(department -> department.clone(department)).collect(Collectors.toList()));
        company.getDepartments().forEach(department -> department.getEmployees().clear());

        companyRepository.saveAndFlush(company);

        //here I assign each employee it's corresponding department ID
        for (int i = 0; i < company.getDepartments().size(); i++) {
            Department departmentInSavedCompany = company.getDepartments().get(i);
            Department departmentWhichStillHasEmployees = departments.get(i);

            departmentWhichStillHasEmployees.setId(departmentInSavedCompany.getId());
            departmentWhichStillHasEmployees.getEmployees().forEach(employee -> employee.setDepartment_id(departmentInSavedCompany.getId()));
        }

        company.getDepartments.clear();
        company.getDepartments.addAll(departments);

        return companyRepository.saveAndFlush(company);
    }
}

@Repository
public interface CompanyRepository extends JpaRepository<Company, Integer> {

}

我目前不喜欢这种实现方式,我也不认为它很好。对于这种情况哪种方法是正确的?

1 个答案:

答案 0 :(得分:1)

使用JPA时,请勿使用ID,而应使用对象引用。

在您的情况下,这意味着删除重复引用的id属性。

为了获得正确的ID实体,请使用JpaRepository.getOne。如果实体已经在第一级缓存中,它将返回该实体,或者返回仅包装ID的代理,这样它将不会访问数据库。

这使您可以组装对象图并将其持久化,从没有引用其他实体的实体开始。

如果您将实体视为同一聚合的一部分,也可以考虑配置级联,即应将它们加载并持久保存在一起。