I should return a SetMapper instance that maps whole result set to a set of Employee instances. f an Employee has a Manager it should contain it as Employee instance as well.
I wrote this implementation of method:
public SetMapper<Set<Employee>> employeesSetMapper() {
return resultSet -> {
Set<Employee> employees = new HashSet<>();
Map<BigInteger, Employee> employeeMap = new HashMap<>();
while (resultSet.next()) {
BigInteger id = BigInteger.valueOf(resultSet.getInt(1));
FullName fullName = new FullName(resultSet.getString("firstName"), resultSet.getString("lastName"),
resultSet.getString("middleName"));
Position position = Position.valueOf(resultSet.getString("position"));
LocalDate hired = resultSet.getDate(7).toLocalDate();
BigDecimal salary = resultSet.getBigDecimal("salary");
BigInteger managerId = BigInteger.valueOf(resultSet.getInt(6));
Employee manager = null;
if (managerId != null) {
manager = employeeMap.get(managerId);
}
Employee employee = new Employee(id, fullName, position, hired, salary, manager);
employees.add(employee);
employeeMap.put(id, employee);
}
return employees;
};
}
But when I run my tests, some managers equal to null even though Employee has one.
The problem is that the above logic is dependent on the order of the results. Only if each manager record is returned before the records of every employee that reports to that manager, the above logic will work. Instead if you keep track of each employee's manager id in a separate map and apply it in a separate loop after the existing while loop, it should work irrespective of the order of the results. Something like below:
public SetMapper<Set<Employee>> employeesSetMapper() {
return resultSet -> {
Set<Employee> employees = new HashSet<>();
Map<BigInteger, Employee> employeeMap = new HashMap<>();
Map<BigInteger, BigInteger> employeeToManagerMap = new HashMap<>();
while (resultSet.next()) {
BigInteger id = BigInteger.valueOf(resultSet.getInt(1));
FullName fullName = new FullName(resultSet.getString("firstName"), resultSet.getString("lastName"),
resultSet.getString("middleName"));
Position position = Position.valueOf(resultSet.getString("position"));
LocalDate hired = resultSet.getDate(7).toLocalDate();
BigDecimal salary = resultSet.getBigDecimal("salary");
BigInteger managerId = BigInteger.valueOf(resultSet.getInt(6));
if (managerId != null && !managerId.equals(BigInteger.ZERO)) {
employeeToManagerMap.put(id, managerId);
}
Employee employee = new Employee(id, fullName, position, hired, salary, null);
employees.add(employee);
employeeMap.put(id, employee);
}
employeeToManagerMap.forEach((key, value) -> {
Employee employee = employeeMap.get(key);
Employee manager = employeeMap.get(employeeToManagerMap.get(key));
employee.setManager(manager);
});
return employees;
};
}
UPDATE:
Use the below solution when the Employee
class has getters, but no setters:
public SetMapper<Set<Employee>> employeesSetMapper() {
return resultSet -> {
Map<BigInteger, Employee> employeeMap = new HashMap<>();
Map<BigInteger, BigInteger> employeeToManagerMap = new HashMap<>();
while (resultSet.next()) {
BigInteger id = BigInteger.valueOf(resultSet.getInt(1));
FullName fullName = new FullName(resultSet.getString("firstName"), resultSet.getString("lastName"),
resultSet.getString("middleName"));
Position position = Position.valueOf(resultSet.getString("position"));
LocalDate hired = resultSet.getDate(7).toLocalDate();
BigDecimal salary = resultSet.getBigDecimal("salary");
BigInteger managerId = BigInteger.valueOf(resultSet.getInt(6));
if (managerId != null && !managerId.equals(BigInteger.ZERO)) {
employeeToManagerMap.put(id, managerId);
}
Employee employee = new Employee(id, fullName, position, hired, salary, null);
employeeMap.put(id, employee);
}
employeeToManagerMap.forEach((key, value) -> {
employeeMap.put(key, getEmployeeWithManager(key, employeeMap, employeeToManagerMap));
});
return new HashSet<>(employeeMap.values());
};
}
private static Employee getEmployeeWithManager(BigInteger employeeId, Map<BigInteger, Employee> employeeMap,
Map<BigInteger, BigInteger> employeeToManagerMap) {
Employee employee = employeeMap.get(employeeId);
Employee manager = employeeMap.get(employeeToManagerMap.get(employeeId));
Employee employeeWithManager;
if (null != manager) {
employeeWithManager = new Employee(
employee.getId(), employee.getFullName(), employee.getPosition(), employee.getHireDate(),
employee.getSalary(), getEmployeeWithManager(manager.getId(), employeeMap, employeeToManagerMap));
} else {
employeeWithManager = employee;
}
return employeeWithManager;
}
You can find a test verifying this logic on github