Employee Catalog: How to extract fullchain or single value of manager using RowMapper Interface?

I’m writing implementation of Employee Catalog using Java Spring Boot.
Here are the details of instructions:
Create a webapp serving somewhat like a REST catalog of employees stored in embedded database.

Implement endpoints serving employees as JSON representations of springemployeecatalog.domain.* classes.

Each employee record should include information of his department and his manager. Manager’s manager should be usually null.

Endpoints (all serves GET requests):

/employees – list all employees. Supports paging*.

/employees/{employee_id} – single employee. If parameter named full_chain exists and is set to true then full manager chain should be written (include employee`s manager, manager of manager, manager of manager of manager and so on up to the organization head)

/employees/by_manager/{managerId} – list employees who subordinates to the manager. No transitivity. Supports paging*.

/employees/by_department/{departmentId or departmentName} – list employees who is working in the department. Supports paging*.

  • Supports paging – means that you may manage what sublist of employees you want to get by three parameters:

page – number of the page (starts with 0)
size – amount of entry per page
sort – name of the field for sorting (single value from list [lastName, hired, position, salary], order is ascending)
Reminder:

You may not change domain classes
You may not change sql scripts
You should serve employees from the database without any in-memory storage

Here’s the code implementation I wrote and domain classes:

public class Department {

private final Long id;
private final String name;
private final String location;

@JsonCreator
public Department(@JsonProperty("id") final Long id,
                @JsonProperty("name") final String name,
                @JsonProperty("location") final String location) {
    this.id = id;
    this.name = name;
    this.location = location;
}

public Long getId() {
    return id;
}

public String getName() {
    return name;
}

public String getLocation() {
    return location;
}

@Override
public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    final Department that = (Department) o;
    return Objects.equal(id, that.id) &&
            Objects.equal(name, that.name) &&
            Objects.equal(location, that.location);
}

@Override
public int hashCode() {
    return Objects.hashCode(id, name, location);
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
            .add("id", id)
            .add("name", name)
            .add("location", location)
            .toString();
}


public static class Parser {
    private static ObjectMapper mapper = new ObjectMapper();
    static {
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false);
    }

    public static String toJson(Department department){
        try {
            final StringWriter writer = new StringWriter();
            mapper.writeValue(writer, department);
            return writer.toString();
        } catch (IOException exc) {
            throw new RuntimeException(exc);
        }
    }

    public static Department parseJson(String json){
        try {
            return mapper.readValue(json, Department.class);
        } catch (IOException exc) {
            throw new RuntimeException(exc);
        }
    }
}
}
public class Employee {
    private final Long id;
    private final FullName fullName;
    private final Position position;
    private final LocalDate hired;
    private final BigDecimal salary;
    private final Employee manager;
    private final Department department;

@JsonCreator
public Employee(@JsonProperty("id") final Long id,
                @JsonProperty("fullName") final FullName fullName,
                @JsonProperty("position") final Position position,
                @JsonProperty("hired") final LocalDate hired,
                @JsonProperty("salary") final BigDecimal salary,
                @JsonProperty("manager") final Employee manager,
                @JsonProperty("department") final Department department) {
    this.id = id;
    this.fullName = fullName;
    this.position = position;
    this.hired = hired;
    this.salary = salary.setScale(5, RoundingMode.HALF_UP);
    this.manager = manager;
    this.department = department;
}

public Long getId() {
    return id;
}

public FullName getFullName() {
    return fullName;
}

public Position getPosition() {
    return position;
}

public LocalDate getHired() {
    return hired;
}

public BigDecimal getSalary() {
    return salary;
}

public Employee getManager() {
    return manager;
}

public Department getDepartment() {
    return department;
}

@Override
public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    final Employee employee = (Employee) o;
    return Objects.equal(id, employee.id) &&
            Objects.equal(fullName, employee.fullName) &&
            position == employee.position &&
            Objects.equal(hired, employee.hired) &&
            Objects.equal(salary, employee.salary) &&
            Objects.equal(manager, employee.manager) &&
            Objects.equal(department, employee.department);
}

@Override
public int hashCode() {
    return Objects.hashCode(id, fullName, position, hired, salary, manager, department);
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
            .add("id", id)
            .add("fullName", fullName)
            .add("position", position)
            .add("hired", hired)
            .add("salary", salary)
            .add("manager", manager)
            .add("department", department)
            .toString();
}

public static class Parser {
    private static ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false);
    }

    public static String toJson(Employee employee) {
        try {
            final StringWriter writer = new StringWriter();
            mapper.writeValue(writer, employee);
            return writer.toString();
        } catch (IOException exc) {
            throw new RuntimeException(exc);
        }
    }

    public static Employee parseJson(String json) {
        try {
            return mapper.readValue(json, Employee.class);
        } catch (IOException exc) {
            throw new RuntimeException(exc);
        }
    }
}
}
public class FullName {
    private final String firstName;
    private final String lastName;
    private final String middleName;

@JsonCreator
public FullName(@JsonProperty("firstName") final String firstName,
                @JsonProperty("lastName") final String lastName,
                @JsonProperty("middleName") final String middleName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.middleName = middleName;
}

public String getFirstName() {
    return firstName;
}

public String getLastName() {
    return lastName;
}

public String getMiddleName() {
    return middleName;
}

@Override
public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    final FullName fullName = (FullName) o;
    return Objects.equal(firstName, fullName.firstName) &&
            Objects.equal(lastName, fullName.lastName) &&
            Objects.equal(middleName, fullName.middleName);
}

@Override
public int hashCode() {
    return Objects.hashCode(firstName, lastName, middleName);
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
            .add("firstName", firstName)
            .add("lastName", lastName)
            .add("middleName", middleName)
            .toString();
}
}
public enum Position {
PRESIDENT,
MANAGER,
ANALYST,
CLERK,
SALESMAN
 }
@RestController
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeService employeeService;

@Autowired
public EmployeeController(EmployeeService employeeService) {
    this.employeeService = employeeService;
}

@GetMapping
public List<Employee> getAllEmployees(@RequestParam(defaultValue = "0") int page,
                                      @RequestParam int size,
                                      @RequestParam String sort) {
    return employeeService.getAllEmployees(page, size, sort);
}

@GetMapping("/{employee_id}")
public Optional<Employee> getEmployeeById(@PathVariable("employee_id") Long employeeId,
                                          @RequestParam(defaultValue = "false") boolean full_chain) {
    return employeeService.getEmployeeById(employeeId, full_chain);
}

@GetMapping("/by_manager/{managerId}")
public List<Employee> getEmployeeByManager(@PathVariable("managerId") Long managerId,
                                           @RequestParam(defaultValue = "0") int page,
                                           @RequestParam int size,
                                           @RequestParam String sort) {
    return employeeService.getEmployeeByManager(managerId, page, size, sort);
}

@GetMapping("/by_department/{departmentId}")
public List<Employee> getEmployeeByDepartment(@PathVariable("departmentId") Long departmentId,
                                              @RequestParam(defaultValue = "0") int page,
                                              @RequestParam int size,
                                              @RequestParam String sort) {
    return employeeService.getEmployeeByDepartment(departmentId, page, size, sort);
}
}
@Service
public class EmployeeService {
private final EmployeeRepository employeeRepository;

@Autowired
public EmployeeService(EmployeeRepository employeeRepository) {
    this.employeeRepository = employeeRepository;
}

public List<Employee> getAllEmployees(int page, int size, String sort) {
    return employeeRepository.findAll(page, size, sort);
}

public Optional<Employee> getEmployeeById(Long employeeId, boolean fullChain) {
    return employeeRepository.findById(employeeId, fullChain);
}
public List<Employee> getEmployeeByManager(Long managerId, int page, int size, String sort) {
    return employeeRepository.findEmployeesByManager(managerId, page, size, sort);
}

public List<Employee> getEmployeeByDepartment(Long departmentId, int page, int size, String sort) {
    return employeeRepository.findEmployeesByDepartmentId(departmentId, page, size, sort);
}
}
@SpringBootApplication
public class MvcApplication {
public static void main(String[] args) {
    SpringApplication.run(MvcApplication.class, args);
}
}
@Repository
public interface EmployeeRepository{
List<Employee> findAll(int page, int size, String sort);

Optional<Employee> findById(Long id, boolean fullChain);

List<Employee> findEmployeesByManager(Long managerId, int page, int size, String sort);

List<Employee> findEmployeesByDepartmentId(Long departmentId, int page, int size, String sort);
}
@Repository
public class EmployeeRepositoryImpl implements EmployeeRepository{

private final JdbcTemplate jdbcTemplate;

@Autowired
public EmployeeRepositoryImpl(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}

@Override
public List<Employee> findAll(int page, int size, String sort) {
    String query = "SELECT * FROM EMPLOYEE ORDER BY " + sort;
    List<Employee> employees = jdbcTemplate.query(query, new EmployeeRowMapper(jdbcTemplate));
    return getList(employees, page, size);
}



@Override
public Optional<Employee> findById(Long id, boolean fullChain) {
    String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
    Employee employee = jdbcTemplate.queryForObject(query, new Object[]{id}, new EmployeeRowMapper(jdbcTemplate));
    return Optional.ofNullable(employee);
}


@Override
public List<Employee> findEmployeesByManager(Long managerId, int page, int size, String sort) {
    String query = "SELECT * FROM EMPLOYEE WHERE MANAGER = ? ORDER BY " + sort;
    List<Employee> employees = jdbcTemplate.query(query, new Object[]{managerId}, new EmployeeRowMapper(jdbcTemplate));
    return getList(employees, page, size);
}


@Override
public List<Employee> findEmployeesByDepartmentId(Long departmentId, int page, int size, String sort) {
    String query = "SELECT * FROM EMPLOYEE WHERE DEPARTMENT = ? ORDER BY " + sort;
    List<Employee> employees = jdbcTemplate.query(query, new Object[]{departmentId}, new EmployeeRowMapper(jdbcTemplate));
    return getList(employees, page, size);
}
private List<Employee> getList(List<Employee> employees, int page, int size) {
    int startIndex = page * size;
    int endIndex = Math.min(startIndex + size, employees.size());
    return employees.subList(startIndex, endIndex);
}

private static class EmployeeRowMapper implements RowMapper<Employee> {
    private final JdbcTemplate jdbcTemplate;

    private EmployeeRowMapper(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    private static List<Long> employeeManager = new ArrayList<>();

    @Override
    public Employee mapRow(ResultSet resultSet, int i) throws SQLException {
        Long id = resultSet.getLong("ID");
        String firstName = resultSet.getString("FIRSTNAME");
        String lastName = resultSet.getString("LASTNAME");
        String middleName = resultSet.getString("MIDDLENAME");
        FullName fullName = new FullName(firstName, lastName, middleName);
        Position position = Position.valueOf(resultSet.getString("POSITION"));
        LocalDate hired = resultSet.getDate("HIREDATE").toLocalDate();
        BigDecimal salary = BigDecimal.valueOf(resultSet.getDouble("SALARY"));
        Long managerId = resultSet.getLong("MANAGER");
        Employee manager = null;
        if (employeeManager.isEmpty()) {
            manager = findEmployee(managerId);
        }
        Long departmentId = resultSet.getLong("DEPARTMENT");
        Department department = findDepartment(departmentId);
        Employee employee = new Employee(id, fullName, position, hired, salary, manager, department);
        return employee;
    }

    private Department findDepartment(Long departmentId) {
        if (departmentId == 0) return null;
        String query = "SELECT * FROM DEPARTMENT WHERE ID = ?";
        return jdbcTemplate.queryForObject(query, new Object[]{departmentId}, new DepartmentRowMapper());
    }

    private Employee findEmployee(Long managerId) {
        if (managerId == 0) return null;
        employeeManager.add(managerId);
        String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
        return jdbcTemplate.queryForObject(query, new Object[]{managerId}, new EmployeeRowMapper(jdbcTemplate));
    }

    private static class DepartmentRowMapper implements RowMapper<Department> {

        @Override
        public Department mapRow(ResultSet resultSet, int i) throws SQLException {
            Long id = resultSet.getLong("ID");
            String name = resultSet.getString("NAME");
            String location = resultSet.getString("LOCATION");
            return new Department(id, name, location);
        }
    }

}
}

I want to split findEmployee method in RowMapper in two, so one finds full manager chain and one finds only manager of an employee, but doesn’t continue after that. (Employee – Manager of an employee – Manager of manager is set to null). I also have findById method, which takes fullChain value as parameter and I want to connect it to RowMapper somehow.

Right now, I make a list employeeManager which saves Id’s of managers and I check if list is empty and if it is not I leave manager to be null, so manager of manager will be null, but it doesn’t work like that, since list is saved for whole application.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật