【问题标题】:How to correctly save associated entities?如何正确保存关联实体?
【发布时间】:2018-10-15 11:20:14
【问题描述】:

假设我们有以下三个域模型实体: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 为我的company 分配一个ID。我将接收到的 ID 分配给先前保存的 departments 列表的每个实体中的每个 company_id,然后将该列表附加到 company 并执行另一个 saveAndFlush,我再为 @987654347 执行一次@列表。

@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> {

}

我目前不喜欢这个实现,也不觉得它很好。哪种方法适合这种情况?

【问题讨论】:

  • 为什么将引用的 id 作为实体中的单独属性?
  • 因为在发布新员工时,我只需传递部门 id 即可建立关系。 POST { "name" : "New Employee", "department_id" : 1}

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


【解决方案1】:

使用 JPA 时,不要使用 ID,而是使用对象引用。

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

为了获得正确的 ID 实体,请使用 JpaRepository.getOne。如果实体已经在一级缓存中,它将返回实体,或者只是包装 id 的代理,因此它不会命中数据库。

这允许您组装您的对象图,并从没有引用其他实体的实体开始,一次性将其持久化。

如果您认为实体是同一个聚合的一部分,您也可以考虑配置级联,即它们应该一起加载和持久化。

【讨论】:

  • 我已经实现了级联。所以在不使用 ID 的情况下,假设我们发送一个 POST 来创建具有以下 RequestBodydepartment{"name": "depart_name", "employees" : ["name" : "employee_name"],只需将此主体映射到 department 模型并使用DepartmentRepository.saveAndFlush(newDepart) 保存就足够了?
  • 另外,我的请求正文应该如何查找更新部门及其员工,例如{"name": "UpdatedDepart_name", "employees" : ["name" : "UpdatedEmployee_name"],我们可以从Path Variable 获取部门的id,但是我们如何获取要更新的员工的 ID?
  • 对第一个问题:是的。对于第二个:对不起,对于 REST 方面的经验很少给出一个自信的答案,但我猜:如果它有自己的存储库,则传递一个 url。再次抱歉,不知道该怎么做。
  • 但是假设我想添加一个新员工,如果我从它的模型中删除 id 字段,我如何引用它的部门?。像这样的东西? POST{"name" : "createdEmp", "department": {"id":1}}
  • 再说一次,这应该是 id 为 1 的部门的 URL。但我的知识不足以提供确切的细节。
猜你喜欢
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 2019-09-16
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
相关资源
最近更新 更多