visit
To draw a tree of organizational modules is enough for us to be able to get a list of child units. This is a very simple task, just add the findByParent(OrganizationUnit parent) method to the repository. However, in this case, we will query the database for all information about the department, which is redundant. This request will look like this:
SELECT * FROM organization_unit WHERE parent_id=<<parent_id>>;
To draw the hierarchy, we need to get the unit ID and its name only, the rest of the fields are not interested. Accordingly, we could execute the request:
SELECT id, title FROM organization_unit WHERE parent_id=<<parent_id>>;
To execute such a query, you must either write native SQL or use a query builder, such as QueryDSL, which is close to manually writing a query. But there is another option, you can allocate a separate class for a shortened ViewModel that is read-only. You will have to create a separate repository for it, but this will allow you to reuse this ViewModel in various scenarios where only the main fields of the entity are required. The class structure will then look like this:
@Getter
@MappedSuperclass
@SuppressWarnings("unchecked")
@ToString(callSuper = true, onlyExplicitlyIncluded = true)
public abstract class OrganizationUnitBase<T extends OrganizationUnitBase<T>> {
@Id
@ToString.Include
@EqualsAndHashCode.Include
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
@NotNull
@ToString.Include
@Column(name = "title")
protected String title;
//link to parent organization unit for filter by parentId
@Column(name = "parent_id", insertable = false, updatable = false)
protected Long parentId;
}
@Getter
@Setter
@Entity
@NoArgsConstructor
@Accessors(chain = true)
@Table(name = "organization_unit")
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
@ToString(callSuper = true, onlyExplicitlyIncluded = true)
public class OrganizationUnitFull extends OrganizationUnitBase<OrganizationUnitFull> {
private String address;
private String phone;
private String email;
private String fax;
private LocalDate openDate;
private LocalDate closeDate;
private boolean active;
@ManyToOne
private OrganizationUnit parent;
}
@Entity
@Getter
@Setter
@Immutable
@NoArgsConstructor
@Accessors(chain = true)
@Table(name = "organization_unit")
public class OrganizationUnitMinimalView extends OrganizationUnitBase<OrganizationUnitMinimalView> {
}
@Repository
public interface OrganizationUnitMinimalViewRepository extends JpaRepository<OrganizationUnitMinimalView, Long> {
List<OrganizationUnitMinimalView> findAllByParentId(Long parentId);
}
@Repository
public interface OrganizationUnitFullRepository extends JpaRepository<OrganizationUnitFull, Long> {
}
Now let's look at the second part of the problem. We need to see all the employees of the organizational module, indicating the positions in which they work. To do this, we need to perform at least two JOINs. When using full entities, it would look like this:
SELECT *
FROM position AS p
JOIN appointment AS a ON a.position_id=p.id
JOIN employee AS e ON a.employee_id=e.id
WHERE p.organization_unit_id=<<organization_unit_id>>;
In this case, we will get all fields from three tables. And if the organizationUnit field in the Position entity has fetch type eager, then we will get another additional JOIN with the organization_unit table. In reality, it is enough for us to select only the fields of interest to us:
SELECT p.id, p.title, e.id, e.first_name, e.last_name, a.id
FROM position AS p
JOIN appointment AS a ON a.position_id=p.id
JOIN employee AS e ON a.employee_id=e.id
WHERE p.organization_unit_id=<<organization_unit_id>>;
To transform our query into a more optimal view, it is enough to also highlight the truncated ModelViews for the employee entity, position, and assignment. It might look like this:
In conclusion, I would like to say that sometimes such allocations of minified entities have a huge effect on system performance while maintaining code maintainability.