Search code examples
javaspringhibernatejpaspring-data-jpa

How to add some logic before removing entity without @PreRemove?


I found a problem in my project. A have those three entities: CompanyBranch, Department, Employee. Relationship between CompanyBranch and Departmen is One-To-Many, same with Department and Employee. Here my entity classes:

@Entity
@Table(name = "COMPANY_BRANCH", uniqueConstraints = {
        @UniqueConstraint(name = "AddressUniqueConstraint", columnNames = {"COMPANY_BRANCH_CITY",
                "COMPANY_BRANCH_ZIP_CODE", "COMPANY_BRANCH_STREET", "COMPANY_BRANCH_COUNTRY"})
})
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class CompanyBranch {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Convert(
            converter = MoneyConverter.class
    )
    @Column(name = "COMPANY_BRANCH_BUDGET", length = 63)
    private Money budget;

    @Embedded
    @NotNull
    @AttributeOverrides({
            @AttributeOverride(name = "city",
                column = @Column(name = "COMPANY_BRANCH_CITY")),
            @AttributeOverride(name = "zipCode",
                column = @Column(name = "COMPANY_BRANCH_ZIP_CODE")),
            @AttributeOverride(name = "street",
                column = @Column(name = "COMPANY_BRANCH_STREET")),
            @AttributeOverride(name = "country",
            column = @Column(name = "COMPANY_BRANCH_COUNTRY"))
    })
    private Address companyBranchAddress;

    @NotNull
    @NotBlank(message = "Номер телефона не должен быть пустым")
    @Pattern(regexp = "^(\\+\\d{1,3}( )?)?((\\(\\d{1,3}\\))|\\d{1,3})[- .]?\\d{3,4}[- .]?\\d{4}$",
            message = "Неверный формат номера!")
    @Column(name = "COMPANY_BRANCH_PHONE_NUMBER", unique = true)
    private String phoneNumber;
}

Department:

@Entity
@Table(name = "DEPARTMENT")
@Getter
@Setter
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank(message = "Название отдела не может быть пустым!")
    @Column(name = "DEPARTMENT_NAME")
    private String departmentName;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "LAST_MODIFIED")
    private Date lastModified;

    @NotNull
    @NotBlank(message = "Номер телефона не должен быть пустым")
    @Pattern(regexp = "^(\\+\\d{1,3}( )?)?((\\(\\d{1,3}\\))|\\d{1,3})[- .]?\\d{3,4}[- .]?\\d{4}$",
            message = "Неверный формат номера!")
    @Column(name = "DEPARTMENT_PHONE_NUMBER", unique = true)
    private String phoneNumber;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "COMPANY_BRANCH_ID", nullable = false)
    private CompanyBranch companyBranch;

    @PrePersist
    @PreUpdate
    protected void setDefaultLastModified(){
        lastModified = new Date();
    }

}

Employee:

@Entity
@Table(name = "EMPLOYEE")
@Getter
@Setter
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private Name name;

    @Pattern(regexp = "^(\\+\\d{1,3}( )?)?((\\(\\d{1,3}\\))|\\d{1,3})[- .]?\\d{3,4}[- .]?\\d{4}$",
             message = "Неверный формат номера!")
    @NotNull
    @NotBlank(message = "Телефон не может быть пустым!")
    @Column(unique = true, name = "PHONE_NUMBER")
    private String phoneNumber;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "city",
                column = @Column(name = "HOME_CITY")),
            @AttributeOverride(name = "zipCode",
                column = @Column(name = "HOME_ZIP_CODE")),
            @AttributeOverride(name = "street",
                column = @Column(name = "HOME_STREET")),
            @AttributeOverride(name = "country",
                column = @Column(name = "HOME_COUNTRY"))
    })
    private Address homeAddress;

    @Email(message = "Неверный формат почты!")
    @NotNull
    @Column(unique = true, name = "EMAIL")
    private String email;

    @Temporal(value = TemporalType.DATE)
    @Column(name = "EMPLOYMENT_DATE", updatable = false)
    @CreationTimestamp
    private Date employmentDate;

    @BatchSize(size = 4)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "EMPLOYEE_STATUS_ID")
    private EmployeeStatus employeeStatus;

    @NotNull
    @Convert(
            converter = MoneyConverter.class
    )
    @Column(name = "SALARY", length = 63)
    private Money salary;

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

When i am removing department, i want to also change status of each employee in this department, i wanted to do this with @PreRemove annotation, but I think it is not the best way to do so since EmployeeStatus is an entity class, so I need to inject spring logic inside the entity class. So this is the way I managed the problem:

@Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query(value = "UPDATE Employee e SET e.employeeStatus = " +
            "(SELECT es FROM EmployeeStatus es WHERE es.employeeStatus = 'FIRED'), " +
            "e.department = null " +
            "WHERE e.department = :department")
    void updateEmployeesStatusToFiredOnDelete(Department department);

And here is method in Department service:

@Transactional
    public void deleteDepartment(Department department) {
        if (department == null)
            throw new IllegalArgumentException("Illegal department for removal!");

        departmentRepository.updateEmployeesStatusToFiredOnDelete(department);
        departmentRepository.delete(department);
    }

But when I am removing company branch, I want also remove all departments inside it and employees statuses should be changed. As I already said, I dont think that @PreRemove can help me with this. I am thinking about creating a new @Query for company branch repository, but I think it is too inconvinient. Maybe there are some other solutions for solving this problem? Maybe I still can use @PreRemove here somehow?


Solution

    1. Department deletion and employee status change

      In my opinion, you should perform these operations in a single transaction using @Transactional. That is, as you suggested. When an error arises when deleting a deparment, the transaction should be rolled back.

    2. Removing company branch and departments

      I don't think so that @PreRemove is the best option. I would simply in one transaction remove the company branch, all departments from company branch and change the employee status using Repository and Spring Data. I think this is a more readable solution.