JavaFX + SQL: это правильный способ выполнения CRUD для сложных объектов? - PullRequest
0 голосов
/ 11 апреля 2019

Я создал приложение JavaFX для данных о судах, сотрудниках и рабочих местах. Текущая версия работает, но она довольно медленная, которую мне нужно исправить. Я понимаю корень своих проблем, но не знаю, как этого избежать

У меня есть несколько сущностей

class Vessel
{
    IntegerProperty id;
    StringProperty name;
    //and some other fields...
}

class Employee
{
    IntegerProperty id;
    StringProperty name;
    //and some other fields...
} 

class Job
{
    IntegerProperty id;
    StringProperty name;
    IntegerProperty employee;
    //and also a list of vessels assigned to it
    //and many more fields...
}

Эти сущности соответствуют следующим таблицам (PostgreSQL)

CREATE TABLE vessels(id SERIAL PRIMARY KEY, "name" VARCHAR);
CREATE TABLE employees(id SERIAL PRIMARY KEY, "name" VARCHAR);
CREATE TABLE jobs(id SERIAL PRIMARY KEY, "name" VARCHAR);
CREATE TABLE jobs_vessels(id SERIAL PRIMARY KEY, job INT REFERENCES jobs(id), vessel INT REFERENCES vessels(id));

Если я имею дело с Vessel s, Employee s или другими простыми объектами (поля являются простыми типами), я выполняю

SELECT * FROM vessels ORDER BY "name";

, за которым следует стандартный код JDBC с Connection, PreparedStatement и ResultSet. Затем я создаю ObservableList<Vessel> и заполняю его данными из ResultSet. Затем я создаю TableView, инициализирую его TableColumn s (которые в основном TextFieldTableCell) и, наконец, отображаю данные.

Проблема возникает, когда я хочу отобразить \ изменить список сложных объектов (поля имеют тип "класс ..."). Первая часть будет выглядеть так же ... Я исполняю

SELECT * FROM jobs ORDER BY "name";

и затем я конвертирую ResultSet resultSet в ObservableList<Job> jobs.

Мне также нужно назначить суда для каждой работы, поэтому я делаю

protected Map<Job, List<int>> jobsVesselsMap;

//...

for (Job job : jobs)
{
    //1. get list of vessels (ids) assigned to the job via "SELECT * FROM jobs_vessels WHERE job = ?"
    //2. convert ResultSet to List<int>
    //3. store List<int> to "jobsVesselsMap".
}

Поскольку мне нужно изменить назначенные суда и сотрудника (чтобы пользователь мог выбрать их с помощью соответствующих ComboBox s), мне нужно еще 2 списка: список судов и список сотрудников. Я исполняю

SELECT * FROM vessels ORDER BY "name";

и преобразовать ResultSet в ObservableList<Vessel>. Затем я выполняю

SELECT * FROM employees ORDER BY "name";

и преобразовать ResultSet в ObservableList<Employee>.

Теперь идет медленная часть. Поскольку полный объект "задание" должен содержать фактические данные о судах и сотрудниках, мне нужно поменять идентификаторы на реальные объекты. Полное задание представлено объектом типа JobEx

class JobEx
{
    IntegerProperty id;
    StringProperty name;
    ObjectProperty<Employee> employee;
    ListProperty<Vessel> vessels;
    //and many more fields...
}

Я конвертирую ObservableList<Job> в ObservableList<JobEx>, вызывая toJobEx в цикле.

public static JobEx toJobEx(Job job,
                                    ObservableList<Vessel> vessels,
                                    ObservableList<Employee> employees,
                                    List<int> jobVesselsIds)
{
    JobEx jobEx = new JobEx();
    jobEx.setId(job.getId());
    jobEx.setName(job.getName());

    //employee
    Optional<Employee> employee = employees.stream().filter(e -> e.getId() == job.getEmployee()).findFirst();

    if (employee.isPresent())
    {
        jobEx.setVessel(employee.get());
    }

    //vessels
    ObservableList<Vessel> jobVessels = FXCollections.observableArrayList();

    for (int vesselId : jobVesselsIds)
    {
        Optional<Vessel> vessel = vessels.stream().filter(v -> v.getId() == vesselId).findFirst();

        if (vessel.isPresent())
        {
            jobEx.setVessel(vessel.get());
        }
    }

    jobEx.setVessels(jobVessels);

    return jobEx;
}

//...

ObservableList<JobEx> jobExs = FXCollections.observableArrayList();

for (Job job : jobs)
{
    jobExs.add(toJobEx(job));
}

На этом шаге у меня есть полный список работ, и я начинаю его отображать.

Я создаю TableView, создаю TableColumns, устанавливаю соответствующие редакторы для его ячеек (TextFieldTableCell для StringProperty s, ComboBoxTableCell для ObjectProperty<Employees>, пользовательский редактор (несколько комбинированных списков + кнопки) для ` ListProperty.

Когда я хочу сохранить данные, я выполняю этот процесс в обратном порядке: ObservableList<JobEx> -> ObservableList<Job> -> сохранить в нескольких таблицах.

Теперь в выпадающих списках внутри таблицы правильно отображаются выбранные значения, поскольку поля каждого экземпляра JobEx и членов списков, которые использовались для заполнения этих выпадающих списков, указывают на одну и ту же память и одинаковые адреса, что означает, что выпадающие списки автоматически выбирают значения, что и является Я хочу достичь.

Положительной стороной является то, что я могу использовать привязки JavaFX и легко выбирать \ манипулировать значениями через списки, комбинированные списки и аналогичные элементы управления пользовательским интерфейсом. Я не знаю, как добиться того же, сохраняя идентификаторы (без подхода «идентификатор объекта, массив идентификаторов списков объектов»). Недостатком является то, что это преобразование Job в JobEx занимает много времени (иногда слишком много), и в реальном приложении гораздо больше классов "... Ex", которые имеют объекты вместо идентификаторов, что приводит к большому количеству кода и болезненные времена загрузки.

Есть ли способ избежать этого. В идеале, я хотел бы полностью удалить эту часть "в ... Ex преобразование", потому что это медленно и чувствует себя очень неправильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...