1

There is a bidirectional one-to-many relationship between Department and 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> {

}

In the database, I have those records.

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

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

When I tried to delete an Employee(id="3"),

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

But in console, it only printed 2 select statements without deleting:

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=?

And I went to see the database, Nothing has been done.

How does spring-data-jpa works? I'm confused for several days.

Thank you for your answers in advance.

Jack Peng
  • 11
  • 2

3 Answers3

0

CrudRepository has methods delete(<entity>) and deleteById(). You should use deleteById and not the entity.

Karthik R
  • 5,523
  • 2
  • 18
  • 30
  • Thank you! I have changed it to extend CrudRepository, but also only has delete() and delete method. And I have tried to use CurdRepository.delete(), but it also doesn't work and has the same result. – Jack Peng Oct 31 '19 at 11:04
  • DeleteById not delete(id)? – Karthik R Oct 31 '19 at 12:11
  • Oh, It's because the spring-data-JPA version is too low and I changed it to the newest. Using `deleteById()`, but it also doesn't work, I can't delete the employee from database. – Jack Peng Nov 01 '19 at 02:23
  • What's the generated SQL? – Karthik R Nov 01 '19 at 02:31
  • It's the same. ```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=?``` – Jack Peng Nov 01 '19 at 08:23
0

Because @Transactional Spring tests are by default marked for Rollback only and SQL changes are typically only flushed to the database on transaction commit.

You will then need to either (1) manually flush the changes to force a database write:

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
    }

or (2)

Set the transaction as not being for rollback only which you can do in various ways, including use of @Commit and @Rollback annotations:

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

Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • Thank you! I have added @Commit to my test method, and I can delete the department in the test now, but I can't delete the employee yet. When I delete the employee, there are also the same results in the console **without delete statement**. Actually, In production env, I can delete the department but can't delete employee. So I want to figure out how to delete employee and why. – Jack Peng Oct 31 '19 at 16:04
  • You can't keep asking another question. I feel this one has been answered so you should mark it as accepted and and create a new question for your 2nd issue - **including the relevant repository code**. I would guess from your code that maybe this is relevant: https://stackoverflow.com/questions/56583707/hibernate-ondelete-cascade-in-test-doesnt-work/56594151#56594151 Deleting Department using a query rather than via `em.remove()` will not cascade the delete to employees. – Alan Hay Oct 31 '19 at 18:34
0

I have found that once the department knows who is related to it, you can't delete employees who are related to the department. I use fetch=FetchType.EAGER on both sides, so when I delete the employee, it will load the department and the employees of the department. The right thing is that you need to remove the employee from the department's employees, then delete it. You can try this:

    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");
    }
Jack Peng
  • 11
  • 2