0

I have 2 entities/model. Department and Employee

Relationship is 1 Department has MANY Employee.

My understanding of how to go about implementing a Bi-directional relationship of entities is:

  1. Identify the Parent and Child
  2. Identify the owner of relationship (in this case, Department owns Employee). Then add mappedBy='fieldname'
  3. Annotate the both entities with inverse. @OneToMany on 1st entity and @ManyToOne on the 2nd entity

Below is how I designed the app.

Model

Department

@Entity
public class Department {

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

    @OneToMany(mappedBy = "department")
    private Set<Employee> employees;

    //getters and setters...
}

Employee

@Entity
public class Employee {

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

    private String firstName;
    private String middleName;
    private String lastName;

    @ManyToOne
    @JsonIgnore
    private Department department;
}

Now, if I GET department by id (e.g. localhost:8080/department/1) I'll get :

{
    "id": 1,
    "departmentName": "Accounting",
    "employees": [
        {
            "id": 1,
            "firstName": "John",
            "middleName": "Johnny",
            "lastName": "Doe"
        }
    ]
}

which is fine because I can see the Employee(s) who belongs to a specific Department.

However, on the employee side, if I GET employee by id (e.g. localhost:8080/employee/1) I would get a json result which only displays the employee name BUT does NOT include the Department where the Employee belongs.

{
    "id": 1,
    "firstName": "John",
    "middleName": "Johnny",
    "lastName": "Doe"
}

I had to add @JsonIgnore to department field in Employee class to avoid stackoverflow/endless nested json serialization when doing a GET department (e.g. localhost:8080/department/1)

I have 2 questions :

  1. How do I also display the department information when doing a GET employee by id?
  2. Do I need to create a custom query in the Repository interface using @Query instead? At least for the purpose of displaying the Department name along with Employee when I do a GET Employee by id?

Thank you.

heisenberg
  • 1,784
  • 4
  • 33
  • 62
  • 1
    You have to introduce DTOs for your Employee and Department. Yes, they will have the very same fields but this approach gives your flexibility what to fetch/display and what not. I.e. EmployeeDto might have only Department name not the entire structure. Don't put json annotations on the entities, it is not their resposibility. For bean mapping there are multiple tools, my favourite is MapStruct. This way you won't need any specific queries. Also, promoting my article (the link at the profile) for more details. – Andriy Slobodyanyk Jan 24 '22 at 07:13
  • @AndriySlobodyanyk, thanks! I'll read through it. I haven't used `MapStruct` before. I'll give it a try. – heisenberg Jan 24 '22 at 07:53

1 Answers1

0

You have to use DTOs in your project. DTO stand for data transfer object. As the name suggest it carries data between program layers, APIs, etc. - a read on dto. Department dto may look like this for example:

public class DepartmentDto {

    private Long id;
    private String name;

    //getters setters
}

Employee dto:

public class EmployeeDto {

    private Long id;
    private String firstName;
    private DepartmentDto department;

    //getters setters
}

Then you controller method to get employee by id may look like this:

    @GetMapping("/employee/{employeeId}")
    public EmployeeDto findEmployeeById(@PathVariable Long employeeId) {
        Employee employee = this.employeeRepository.findById(employeeId).orElseThrow(() -> new RuntimeException("Employee not found"));
        DepartmentDto departmentDto = new DepartmentDto();
        departmentDto.setId(employee.getDepartment().getId());
        departmentDto.setName(employee.getDepartment().getDepartmentName());
        EmployeeDto employeeDto = new EmployeeDto();
        employeeDto.setId(employee.getId());
        employeeDto.setFirstName(employee.getFirstName());
        employeeDto.setDepartment(departmentDto);
        return employeeDto;
    }

Like this you won't get stackoverflow. Also mapping entity into a dto normally is not done by hand as in this example, but by third party libraries. You can read for some of them here - java mapping libraries.

Sometimes you may get stuck with circular references in DTO's as well. You can handle them using @JsonIdentityInfo for example. In this question you can find several options how to handle them if you need to.

Chaosfire
  • 4,818
  • 4
  • 8
  • 23
  • Thanks! for your answer. I read about DTOs too while I was working on this. I will try your suggested solution. – heisenberg Jan 24 '22 at 07:52
  • @jordan You are wellcome. I edited my answear with info how to handle circular refrences, because sometimes you get them in DTO's as well. – Chaosfire Jan 24 '22 at 08:01