【问题标题】:How can I delete item which is on multiple side in spring-data-jpa?如何删除 spring-data-jpa 中位于多个侧面的项目?
【发布时间】:2019-10-31 10:12:16
【问题描述】:

Department 和 Employee 之间存在双向的一对多关系。

@Setter
@Getter
@Entity
@Table(name = "t_department")
public class Department {
    @Id
    private String id;

    private String name;

    @OneToMany(mappedBy = "department",fetch = FetchType.EAGER,cascade = CascadeType.ALL)
    private List<Employee> employees;
}


@Setter
@Getter
@Entity
@Table(name = "t_employee")
public class Employee {
    @Id
    private String id;

    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "dept_id")
    private Department department;
}

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, String> {

}

在数据库中,我有那些记录。

t_department:
+----+------------+
| id | name       |
+----+------------+
| 2  | accounting |
| 3  | logistics  |
+----+------------+

t_employee:
+----+------+---------+
| id | name | dept_id |
+----+------+---------+
| 3  | Tom  | 2       |
| 4  | Tina | 3       |
+----+------+---------+

当我试图删除一个 Employee(id="3") 时,

    @Test
    @Transactional
    public void should_delete_employee_success_when_delete_employee_given_a_exist_employee_id_in_DB() {
        employeeRepository.delete("3");
    }

但在控制台中,它只打印了 2 个选择语句没有删除:

Hibernate: select employee0_.id as id1_2_0_, employee0_.dept_id as dept_id3_2_0_, employee0_.name as name2_2_0_, department1_.id as id1_1_1_, department1_.name as name2_1_1_ from t_employee employee0_ left outer join t_department department1_ on employee0_.dept_id=department1_.id where employee0_.id=?
Hibernate: select employees0_.dept_id as dept_id3_2_0_, employees0_.id as id1_2_0_, employees0_.id as id1_2_1_, employees0_.dept_id as dept_id3_2_1_, employees0_.name as name2_2_1_ from t_employee employees0_ where employees0_.dept_id=?

我去看看数据库,什么都没做。

spring-data-jpa 是如何工作的?我迷茫了好几天。

提前感谢您的回答。

【问题讨论】:

    标签: java mysql spring-data-jpa


    【解决方案1】:

    CrudRepository 有方法delete(&lt;entity&gt;)deleteById()。您应该使用 deleteById 而不是实体。

    【讨论】:

    • 谢谢!我已将其更改为扩展 CrudRepository,但也只有 delete() 和 delete 方法。而且我尝试使用 CurdRepository.delete(),但它也不起作用并且具有相同的结果。
    • DeleteById 不删除(id)?
    • 哦,原来是spring-data-JPA版本太低,我换最新了。使用deleteById(),但它也不起作用,我无法从数据库中删除员工。
    • 生成的 SQL 是什么?
    • 是一样的。 Hibernate: select employee0_.id as id1_1_0_, employee0_.dept_id as dept_id3_1_0_, employee0_.name as name2_1_0_, department1_.id as id1_0_1_, department1_.name as name2_0_1_ from t_employee employee0_ left outer join t_department department1_ on employee0_.dept_id=department1_.id where employee0_.id=? Hibernate: select employees0_.dept_id as dept_id3_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id3_1_1_, employees0_.name as name2_1_1_ from t_employee employees0_ where employees0_.dept_id=?
    【解决方案2】:

    因为@Transactional Spring 测试默认标记为仅回滚,并且 SQL 更改通常仅在事务提交时刷新到数据库。

    然后您将需要 (1) 手动刷新更改以强制写入数据库:

    public class SomeTest{
    
        @PersistenceContext 
        private EntityManager em;
    
        @Test
        @Transactional
        public void should_delete_employee_success_when_delete_employee_given_a_exist_employee_id_in_DB() {
            employeeRepository.delete("3");
            em.flush(); //force db update however transaction will still be rolled back
        }
    

    或 (2)

    将事务设置为不只用于回滚,您可以通过多种方式执行此操作,包括使用@Commit@Rollback 注释:

    https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing-annotations-spring

    【讨论】:

    • 谢谢!我已经在我的测试方法中添加了@Commit,我现在可以删除测试中的部门,但我还不能删除员工。当我删除员工时,控制台中也有相同的结果没有删除语句。实际上,在生产环境中,我可以删除部门但不能删除员工。所以我想弄清楚如何删除员工以及为什么。
    • 你不能再问另一个问题了。我觉得这个问题已得到解答,因此您应该将其标记为已接受,并为您的第二期创建一个新问题 - 包括相关的存储库代码。我从您的代码中猜想这可能是相关的:stackoverflow.com/questions/56583707/… 使用查询而不是通过em.remove() 删除部门不会将删除级联到员工。
    【解决方案3】:

    我发现一旦部门知道了与它相关的人员,就无法删除与该部门相关的员工。我两边都用fetch=FetchType.EAGER,所以当我删除员工的时候,会加载部门和部门的员工。正确的做法是,您需要从部门的员工中删除该员工,然后将其删除。你可以试试这个:

        Optional<Employee> employeeOpt = employeeRepository.findById("3");
        if (employeeOpt.isPresent()) {
            Employee employee = employeeOpt.get();
            employee.getDepartment().getEmployees().removeIf(emp -> emp.getId().equals("3"));
            employeeRepository.deleteById("3");
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-03
      • 1970-01-01
      • 2017-02-24
      • 2016-11-12
      • 2020-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多