Как создавать `вложенные` объекты в программном слое работы с БД без ORM? Доброго времени суток. Возникла проблема создания вложенных объектов (класс является полем ещё одного класса) при получении коллекции из базы данных. Например 3 класса: Сотрудники (Employee), Профессии (Profession), Отделы (Department).Упрощённый код (на Java)class Profession { long id; String name; } class Employee { long id; String name; Profession profession; } class Department { long id; String name; List employees; } Данные хранятся в БД MySql в 4 таблицах (согласно нормальным формам). Созданы следующим скриптом:Скрипт создания таблиц-- Профессии CREATE TABLE Professions ( id BIGINT AUTO_INCREMENT NOT NULL, name VARCHAR(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (id), UNIQUE (name) ) ENGINE = InnoDB ROW_FORMAT = DEFAULT CHARACTER SET utf8; -- Сотрудники CREATE TABLE Employees ( id BIGINT AUTO_INCREMENT NOT NULL, name VARCHAR(255) CHARACTER SET utf8 NOT NULL, professionId BIGINT, PRIMARY KEY (id), CONSTRAINT EmployeeToProfession FOREIGN KEY (professionId) REFERENCES Professions (id) ON UPDATE RESTRICT ON DELETE RESTRICT ) ENGINE = InnoDB ROW_FORMAT = DEFAULT CHARACTER SET utf8; -- Отделы CREATE TABLE Departments ( id BIGINT AUTO_INCREMENT NOT NULL, name VARCHAR(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (id), UNIQUE (name) ) ENGINE = InnoDB ROW_FORMAT = DEFAULT CHARACTER SET utf8; -- Таблица связи отдела и сотрудников CREATE TABLE DepartmentsEmployees ( id BIGINT AUTO_INCREMENT NOT NULL, departmentId BIGINT NOT NULL, employeeId BIGINT NOT NULL, PRIMARY KEY (id), UNIQUE (employeeId), CONSTRAINT ConstraintToDepartments FOREIGN KEY (departmentId) REFERENCES Departments (id) ON UPDATE RESTRICT ON DELETE RESTRICT, CONSTRAINT ConstraintToEmployees FOREIGN KEY (employeeId) REFERENCES Employees (id) ON UPDATE RESTRICT ON DELETE RESTRICT ) ENGINE = InnoDB ROW_FORMAT = DEFAULT CHARACTER SET utf8; В системе есть программное `расслоение` классов по пакетам по назначению, сервисы (services) и хранилища (storages). Код приблизительно такой:Набросок слоя сервисов и слоя работы с базой данных// services class ProfessionService { public List getAll() { return new ProfessionStorage().getAll(); } } class EmployeeService { public List getAll() { return new EmployeeStorage().getAll(); } } class DepartmentService { public List getAll() { return new DepartmentStorage().getAll(); } } // storages class ProfessionStorage { public List getAll() { // TODO implement here throw new UnsupportedOperationException(); } } class EmployeeStorage { public List getAll() { // TODO implement here throw new UnsupportedOperationException(); } } class DepartmentStorage { public List getAll() { // TODO implement here throw new UnsupportedOperationException(); } } И, собственно, вопрос - где и как лучше создавать связи между классами? Пока придумал 2 варианта: 1. Передавать зависимые коллекции классов в параметрах методов и при создании объектов выбирать в цикле из списка связанный объект по id:Слой сервисов и слой работы с БД - передача коллекций в параметрах методов// services class ProfessionService { public List getAll() { return new ProfessionStorage().getAll(); } } class EmployeeService { public List getAll() { ProfessionService professionService = new ProfessionService(); List professions = professionService.getAll(); return new EmployeeStorage().getAll(professions); } } class DepartmentService { public List getAll() { EmployeeService employeeService = new EmployeeService(); List employees = employeeService.getAll(); return new DepartmentStorage().getAll(employees); } } // storages class ProfessionStorage { public List getAll() { // TODO implement here throw new UnsupportedOperationException(); } } class EmployeeStorage { public List getAll(List professions) { // TODO implement here throw new UnsupportedOperationException(); } } class DepartmentStorage { public List getAll(List employees) { // TODO implement here throw new UnsupportedOperationException(); } } 2. Возврат из метода Map вместо List, в котором в качестве Value указывается id связанной записи. Связь объектов осущетсвляется в методе слоя сервисов:Слой сервисов и слой работы с БД - озврат из методов хранилища Map вместо List// services class ProfessionService { public List getAll() { return new ProfessionStorage().getAll(); } } class EmployeeService { public List getAll() { ProfessionService professionService = new ProfessionService(); List professions = professionService.getAll(); Map employeesWithProfessionId = new EmployeeStorage().getAll(); List employees = new ArrayList<>(); for (Map.Entry entry : employeesWithProfessionId.entrySet()) { Employee employee = entry.getKey(); Long professionId = entry.getValue(); for (Profession profession : professions) { if (profession.id == professionId) { employee.profession = profession; break; } } employees.add(employee); } return employees; } } class DepartmentService { public List getAll() { EmployeeService employeeService = new EmployeeService(); List employees = employeeService.getAll(); Map> departmentsWithEmployeesId = new DepartmentStorage().getAll(); List departments = new ArrayList<>(); for (Map.Entry> entry : departmentsWithEmployeesId.entrySet()) { Department department = entry.getKey(); List employeeIds = entry.getValue(); department.employees = new ArrayList<>(); for (Long employeeId : employeeIds) { for (Employee employee : employees) { if (employee.id == employeeId) { department.employees.add(employee); break; } } } departments.add(department); } return departments; } } // storages class ProfessionStorage { public List getAll() { // TODO implement here throw new UnsupportedOperationException(); } } class EmployeeStorage { public Map getAll() { // TODO implement here throw new UnsupportedOperationException(); } } class DepartmentStorage { public Map> getAll() { // TODO implement here throw new UnsupportedOperationException(); } } Ни одно из решений особо не нравятся. Как поступить лучше?
Для создания вложенных объектов без использования ORM, можно использовать следующий подход:
Создать отдельный метод в каждом хранилище (storage) для получения связанных данных. Например, для получения сотрудников с их профессиями и отделами.
Использовать возвращаемый тип данных Map<Long, Object>, где ключом будет идентификатор объекта, а значением сам объект. Таким образом, для каждого объекта можно легко получить связанные данные по его идентификатору.
В слое сервисов (services), после получения всех объектов из базы данных, создать связи между объектами, используя полученные данные из хранилищ.
Пример кода:
// storages class EmployeeStorage { public Map<Long, Employee> getAllEmployeesWithProfessionsAndDepartments() { // TODO implement here throw new UnsupportedOperationException(); } } class DepartmentStorage { public Map<Long, Department> getAllDepartmentsWithEmployees() { // TODO implement here throw new UnsupportedOperationException(); } } // services class EmployeeService { public List<Employee> getAllEmployeesWithProfessionsAndDepartments() { Map<Long, Employee> employeesWithProfessionsAndDepartments = new EmployeeStorage().getAllEmployeesWithProfessionsAndDepartments(); // Retrieve professions ProfessionService professionService = new ProfessionService(); List<Profession> professions = professionService.getAll(); // Retrieve departments DepartmentService departmentService = new DepartmentService(); List<Department> departments = departmentService.getAll(); // Create relationships between employees, professions and departments List<Employee> employees = new ArrayList<>(); for (Employee employee : employeesWithProfessionsAndDepartments.values()) { Long professionId = employee.getProfession().getId(); Long departmentId = employee.getDepartment().getId(); for (Profession profession : professions) { if (profession.getId() == professionId) { employee.setProfession(profession); break; } } for (Department department : departments) { if (department.getId() == departmentId) { employee.setDepartment(department); break; } } employees.add(employee); } return employees; } } class DepartmentService { public List<Department> getAll() { Map<Long, Department> departmentsWithEmployees = new DepartmentStorage().getAllDepartmentsWithEmployees(); // Retrieve employees EmployeeService employeeService = new EmployeeService(); List<Employee> employees = employeeService.getAllEmployeesWithProfessionsAndDepartments(); // Create relationships between departments and employees List<Department> departments = new ArrayList<>(); for (Department department : departmentsWithEmployees.values()) { List<Long> employeeIds = department.getEmployeeIds(); department.setEmployees(new ArrayList<>()); for (Long employeeId : employeeIds) { for (Employee employee : employees) { if (employee.getId() == employeeId) { department.getEmployees().add(employee); break; } } } departments.add(department); } return departments; } }
Таким образом, вы сможете эффективно и удобно создавать вложенные объекты без использования ORM.
Для создания вложенных объектов без использования ORM, можно использовать следующий подход:
Создать отдельный метод в каждом хранилище (storage) для получения связанных данных. Например, для получения сотрудников с их профессиями и отделами.
Использовать возвращаемый тип данных Map<Long, Object>, где ключом будет идентификатор объекта, а значением сам объект. Таким образом, для каждого объекта можно легко получить связанные данные по его идентификатору.
В слое сервисов (services), после получения всех объектов из базы данных, создать связи между объектами, используя полученные данные из хранилищ.
Пример кода:
// storagesclass EmployeeStorage {
public Map<Long, Employee> getAllEmployeesWithProfessionsAndDepartments() {
// TODO implement here
throw new UnsupportedOperationException();
}
}
class DepartmentStorage {
public Map<Long, Department> getAllDepartmentsWithEmployees() {
// TODO implement here
throw new UnsupportedOperationException();
}
}
// services
class EmployeeService {
public List<Employee> getAllEmployeesWithProfessionsAndDepartments() {
Map<Long, Employee> employeesWithProfessionsAndDepartments = new EmployeeStorage().getAllEmployeesWithProfessionsAndDepartments();
// Retrieve professions
ProfessionService professionService = new ProfessionService();
List<Profession> professions = professionService.getAll();
// Retrieve departments
DepartmentService departmentService = new DepartmentService();
List<Department> departments = departmentService.getAll();
// Create relationships between employees, professions and departments
List<Employee> employees = new ArrayList<>();
for (Employee employee : employeesWithProfessionsAndDepartments.values()) {
Long professionId = employee.getProfession().getId();
Long departmentId = employee.getDepartment().getId();
for (Profession profession : professions) {
if (profession.getId() == professionId) {
employee.setProfession(profession);
break;
}
}
for (Department department : departments) {
if (department.getId() == departmentId) {
employee.setDepartment(department);
break;
}
}
employees.add(employee);
}
return employees;
}
}
class DepartmentService {
public List<Department> getAll() {
Map<Long, Department> departmentsWithEmployees = new DepartmentStorage().getAllDepartmentsWithEmployees();
// Retrieve employees
EmployeeService employeeService = new EmployeeService();
List<Employee> employees = employeeService.getAllEmployeesWithProfessionsAndDepartments();
// Create relationships between departments and employees
List<Department> departments = new ArrayList<>();
for (Department department : departmentsWithEmployees.values()) {
List<Long> employeeIds = department.getEmployeeIds();
department.setEmployees(new ArrayList<>());
for (Long employeeId : employeeIds) {
for (Employee employee : employees) {
if (employee.getId() == employeeId) {
department.getEmployees().add(employee);
break;
}
}
}
departments.add(department);
}
return departments;
}
}
Таким образом, вы сможете эффективно и удобно создавать вложенные объекты без использования ORM.