Hi,
I have this scenario that one user is actually a department manager and he is standing in the head of a pyramid.
The pyramid depth level is unknown, can be one, or can be 4.
I want to create a user, define his manager.
End of the day, in the reports I want to see an aggregation of a user and the performance of his employees/users.
What are the best practices in this case? is done in a demo project?
Hi,
I cannot remember an example for it, but I would create a new entity called Department with a field User manager and List<User> employees which has a @ManyToMany cardinality.
After that you’ll be able to find all departments where the current user is a manager and all employees of this department.
Will that work for your case?
You need an API that will return:
a) All managers for the user (For C A and B will be returned)
b) All subordinates of the user (for A all subordinates of A + all subordinates of B + all subordinates of C will be returned)
A new entity Employee with a one-to-one association to User is created. The Employee has a reference to a parent employee and subordinates.
@Table(name = "SAMPLE_EMPLOYEE")
@Entity(name = "sample$Employee")
@NamePattern("%s|user")
public class Employee extends StandardEntity {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "USER_ID")
protected User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MANAGER_ID")
protected Employee manager;
@OneToMany(mappedBy = "manager")
protected List<Employee> subordinates;
public List<Employee> getSubordinates() {
return subordinates;
}
public void setSubordinates(List<Employee> subordinates) {
this.subordinates = subordinates;
}
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
An entity listener that automatically creates a related employee when a new user is created:
/**
* Listener automatically creates an employee when the user is created
*/
@Component("sample_MyUserEntityListener")
public class MyUserEntityListener implements BeforeInsertEntityListener<User> {
@Inject
protected Metadata metadata;
@Override
public void onBeforeInsert(User user, EntityManager entityManager) {
Employee employee = metadata.create(Employee.class);
employee.setUser(user);
entityManager.persist(employee);
}
}
A middleware service that returns all subordinates and managers for the selected employee.
@Service(OrgStructureService.NAME)
public class OrgStructureServiceBean implements OrgStructureService {
@Inject
protected DataManager dataManager;
@Inject
protected EntityStates entityStates;
@Override
public List<Employee> loadAllManagers(Employee employee) {
List<Employee> managers = new ArrayList<>();
if (!entityStates.isLoadedWithView(employee, "employee-with-manager")) {
employee = dataManager.reload(employee, "employee-with-manager");
}
while (employee.getManager() != null) {
managers.add(employee.getManager());
employee = dataManager.reload(employee.getManager(), "employee-with-manager");
}
return managers;
}
@Override
public List<Employee> loadAllSubordinates(Employee manager) {
return loadAllSubordinates(Collections.singletonList(manager));
}
protected List<Employee> loadAllSubordinates(List<Employee> managers) {
List<Employee> subordinates = dataManager.load(Employee.class)
.query("select s from sample$Employee e join e.subordinates s where e.id in :managers")
.parameter("managers", managers)
.view("employee-with-subordinates")
.list();
if (!subordinates.isEmpty()) {
subordinates.addAll(loadAllSubordinates(subordinates));
}
return subordinates;
}
}
Each employee returned by the service has a user property, so you can easily get a User entity from it.
You can test the service using the Org Structure Test Screen that is available from the menu.