See this similar question.
Also see this presentation about database antipatterns by Bill Karwin. From slide 48 onwards the 'Naive Tree' antipattern is discussed.
Solution #1: Path enumeration.
Store the path of ancestors (managers in your case):
ID, PATH, NAME
1, 1/, george
2, 2/, peter
3, 1/3, harry
4, 1/3/4, bertrand
Easy to query descendants of george:
SELECT * from Employee WHERE path LIKE '1/%';
The presentation also mentions other solutions which I think are less useful in your case:
- Solution #2: Nested sets
- Solution #3: Closure table
EDIT: here's another idea mixing two database queries with a recursive in-memory search.
public static class Employee {
private Long id;
private boolean isManager;
private Employee manager;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean isManager() {
return isManager;
}
public void setIsManager(boolean isManager) {
this.isManager = isManager;
}
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
}
First get all managers in the database. If the total number of Employees is 16k then the number of managers should be manageable (no pun intended).
// gets all existing managers from the database
private static List<Employee> getAllManagers() {
// SELECT * FROM Employee WHERE isManager = true;
return new ArrayList<>();
}
Then iterate over all managers and recursively determine which managers work under the query manager.
private static Set<Employee> findSubordinateManagers(Employee queryManager) {
List<Employee> allManagers = getAllManagers();
Set<Employee> subordinateManagers = new HashSet<>();
for (Employee employee : allManagers) {
if (isSubordinateTo(employee, queryManager)) {
subordinateManagers.add(employee);
}
}
return subordinateManagers;
}
// determines if the given employee is subordinate to the given manager
private static boolean isSubordinateTo(Employee employee, Employee manager) {
if (employee.getManager() == null) {
return false;
}
if (employee.getManager().getId().equals(manager.getId())) {
return true;
}
return isSubordinateTo(employee, employee.getManager());
}
Then do a second SQL query to get all the employees directly managed by the set of selected managers:
// finds all employees directly subordinate to the given set of managers
private static Set<Employee> findEmployees(Set<Employee> managers) {
// SELECT * from Employee WHERE manager IN (managers);
return new HashSet<>();
}