I'm having self join relationship depicting Employee and manager relationship in my spring data JPA code and exposed it via REST endpoint.

Here is my EmployeeController

@Controller
@RequestMapping(path = "/employee")
public class EmployeeController {
    @Autowired
    private EmployeeRepository employeeRepository;

    @PostMapping

    public Employee addNewUser(@RequestBody Employee employee) {

        return employeeRepository.save(employee);
    }

    @GetMapping
    public @ResponseBody Iterable<Employee> getAllUsers() {
        // This returns a JSON or XML with the users
        return employeeRepository.findAll();
    }
}

Here is the Employee Entity

@Entity
@Table(name = "EMPLOYEE")
public class Employee {

    @Id
    @Column(name = "id")
    @GeneratedValue
    private Long id;

    @Column(name = "fname")
    private String fname;

    @Column(name = "lname")
    private String lname;

    @ManyToOne(cascade = { CascadeType.ALL })
    @JoinColumn(name = "manager_id")
    private Employee manager;

    @OneToMany(mappedBy = "manager")
    private Set<Employee> subordinates = new HashSet<Employee>();

        //getters and setters
}

Now when I submit POST Request to /employee endpoint with below

POST /employee
{
    "fname":"akash",
    "lname":"xavier",
    "manager":{
        "fname":"kiran",
        "lname":"Kumar"
    }
}

it is creating rows as expected in database

mysql> select * from employee;
+----+----------+---------+------------+
| id | fname    | lname   | manager_id |
+----+----------+---------+------------+
| 20 | kiran    | Kumar  |       NULL  |
| 19 | akash    | xavier |         20  |
+----+----------+---------+------------+

Now the problem is I wanted to submit below the request

POST /employee

{
    "fname":"Vipul",
    "lname":"Kumar",
    "manager":{
        "fname":"kiran",
        "lname":"Kumar"
    }
}

but don't want Kiran Kumar manager to get created again.

1 Answers

0
crizzis On

There's no solution that will work out of the box for the use case you've provided. You need to manually test for existing managers by the same name, sth along the lines of:

@PostMapping
public Employee addNewUser(@RequestBody Employee employee) {
    return employeeService.create(employee);
}

Where EmployeeService.create() is implemented as follows:

@Transactional
public void create(Employee employee) {
    Employee manager = Optional.ofNullable(employee.getManager()) 
        .flatMap(managerFromRequest -> employeeRepository.findByFnameAndLname(managerFromRequest.getFname(), managerFromRequest.getLname())
        .ifPresent(Employee:setManager);
    employeeRepository.save(employee);
}

and public Optional<Employee> findByFnameAndLname(String fname, String lname) is declared on EmployeeRepository.

As a side note:

  • @ManyToOne(cascade = { CascadeType.ALL }) is wrong. In particular, CascadeType.REMOVE does not make sense for a @ManyToOne association
  • Are you sure fname and lname are enough to uniquely identify an Employee? Sometimes, people just happen to have the same names