Как реализовать получение и отображение данных с сервера в таблицах TableView?

Помогите понять, что я делаю не так. Создаю на JavaFX приложение для отображения БД на MySQL-сервере. Настраиваю внешний вид страниц с помощью SceneBuilder. Соответственно вот код по классам, которые необходимы для реализации получения данных с сервера и размещения их в таблице TableView.


Проблема в том, что мне удаётся получить данные с сервера и передать их построчно в сущности созданного класса Client, в цикле добавляя их к списку clients. Но данный список я никак не могу поместить/отобразить в TableView.

DBConnection.java


import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import sample.Clients.Client;

import java.sql.*;
import java.util.Date;

public class DBConnection {
    private final Connection connection;

    public DBConnection() throws SQLException {
        String host = "";
        String port = "";
        String name = "";
        String url = "jdbc:mysql://" + host + ":" + port + "/" + name;

        String login = "";
        String password = "";
        connection = DriverManager.getConnection(url, login, password);
    }

    public ObservableList<Client> getClients() throws SQLException, ClassNotFoundException {
        String sql = "SELECT * FROM Clients";
        Statement statement = connection.createStatement();
        ResultSet res = statement.executeQuery(sql);

        ObservableList<Client> clients = FXCollections.observableArrayList();

        while (res.next()) {
            int id = res.getInt("id");
            String legal_entity_name = res.getString("legal_entity_name");
            String bank_details = res.getString("bank_details");

            // переход от реляционной модели к объектной
            clients.add(new Client(id, legal_entity_name, bank_details));
            System.out.println(id + " " + legal_entity_name + " " + bank_details);
        }

        return clients;
    }
}

ClientsController.java


import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import sample.DBConnection;

import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ResourceBundle;

public class ClientsController {

    @FXML
    private ResourceBundle resources;

    @FXML
    private URL location;

    @FXML
    private TableColumn<Client, SimpleIntegerProperty> id;

    @FXML
    private TableColumn<Client, String> legal_entity_name;

    @FXML
    private TableColumn<Client, String> bank_details;

    @FXML
    private TableView<Client> Clients;

    @FXML
    private Button delete;

    @FXML
    private Button update;

    @FXML
    private Button add;

    DBConnection connection = null;

    @FXML
    void initialize() throws SQLException, ClassNotFoundException {
        connection = new DBConnection();
        loadInfo();
    }

    void loadInfo() throws SQLException, ClassNotFoundException {
        for (int i = 0; i < Clients.getItems().size(); i++)
            Clients.getItems().clear();

        id.setCellValueFactory(new PropertyValueFactory<>("id"));
        legal_entity_name.setCellValueFactory(new PropertyValueFactory<>("legal_entity_name"));
        bank_details.setCellValueFactory(new PropertyValueFactory<>("bank_details"));

        // определяем таблицу и устанавливаем данные
        ObservableList<Client> clients = connection.getClients();
        //System.out.println(clients.get(1).getLegalEntityName());
        Clients = new TableView<>(clients);
        Clients.setItems(clients);
        Clients.getColumns().addAll(id, legal_entity_name, bank_details);
    }
}
//System.out.println(clients.get(1).getLegalEntityName());

Если раскомментировать данную строчку, то можно понять, что список clients непустой и верно заполнен передаваемыми данными. Из чего я делаю вывод, что ошибка где-то здесь:

Clients = new TableView<>(clients);
Clients.setItems(clients);
Clients.getColumns().addAll(id, legal_entity_name, bank_details);

Или ещё до получения данных с сервера здесь:

id.setCellValueFactory(new PropertyValueFactory<>("id"));
legal_entity_name.setCellValueFactory(new PropertyValueFactory<>("legal_entity_name"));
bank_details.setCellValueFactory(new PropertyValueFactory<>("bank_details"));

Подскажите, пожалуйста, в чём тут дело? Вроде прописываю по руководствам всё, а данные не выводятся :(


Ответы (1 шт):

Автор решения: Fezas

Я попробую ответить, возможно в чем-то могу ошибаться но Ваш результат я получил. Я использовал паттерн DAO. У Вас не совсем хорошо реализован DBConnection. Он по логике предназначен для организации соединения с БД, а у Вас там и сразу запросы... Даже небольшая программа вскоре доставит при таком подходе массу проблем. DAO (Data Access Objects) - шаблон проектирования, определяющий механизм доступа к данным (ссылка на вики). Грубо говоря, в классах дао (UserDao итд) вы должны организовать методы получения определенных объектов или коллекций этих объектов, а также методы для работы с этими объектами (CRUD-операции)(Evgeny Lazarev). Можете использовать другие паттерны, но это Ваше уже дело. Класс Client для формирования записей

public class Client {
    private Integer id;
    private String legalEntityName;
    private String bankDetails;
    public Client(Integer id, String legalEntityName, String bankDetails) {
        this.id = id;
        this.legalEntityName = legalEntityName;
        this.bankDetails = bankDetails;
    }
    public Integer getId() {return id;}
    public String getLegalEntityName() {return legalEntityName;}
    public String getBankDetails() {return bankDetails;}
    @Override
    public String toString()  {
        return this.legalEntityName;
    }
}

FXML

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="400.0" prefWidth="600.0"
    xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="ПутьКВашемуКонтроллеру.ClientsController">
   <children>
      <TableView fx:id="Clients" layoutX="59.0" layoutY="84.0" prefHeight="200.0" prefWidth="514.0">
        <columns>
          <TableColumn fx:id="id" prefWidth="150.0" text="id" />
          <TableColumn fx:id="legalEntityName" minWidth="0.0" prefWidth="180.0" text="legal_entity_name" />
            <TableColumn fx:id="bankDetails" minWidth="0.0" prefWidth="180.0" text="bank_details" />
        </columns>
      </TableView>
   </children>
</AnchorPane>

Контроллер

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

import java.net.URL;
import java.util.ResourceBundle;

public class ClientsController implements Initializable {
    private static ObservableList<Client> clientData = FXCollections.observableArrayList();
    @FXML
    private TableColumn<Client, String> bankDetails, legalEntityName;
    @FXML
    private TableColumn<Client, Integer> id;
    @FXML
    private TableView<Client> Clients;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initData();
        // устанавливаем тип и значение которое должно хранится в колонке
        id.setCellValueFactory(new PropertyValueFactory<Client, Integer>("id"));
        legalEntityName.setCellValueFactory(new PropertyValueFactory<Client, String>("legalEntityName"));
        bankDetails.setCellValueFactory(new PropertyValueFactory<Client, String>("bankDetails"));
        // заполняем таблицу данными
        Clients.setItems(clientData);
    }
    private void initData() {
        var clients = ClientDAO.getInstance().findAll();
        for (Client client : clients) {
            clientData.add(new Client(
                    client.getId(),
                    client.getLegalEntityName(),
                    client.getBankDetails()
            ));
        }
    }
}

Слой DAO с одной реализацией вывода всех записей

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ClientDAO {
    private static final String FIND_ALL_SQL = """
            SELECT id,
                legal_entity_name,
                bank_details 
            FROM "Clients"
            """;
    public List<Client> findAll() {
        try (var connection = DBConnection.getInstance().getConnection();
             var preparedStatement = connection.prepareStatement(FIND_ALL_SQL)) {
            var resultSet = preparedStatement.executeQuery();
            List<Client> clients = new ArrayList<>();
            while (resultSet.next()) {
                clients.add(buildClient(resultSet));
            }
            return clients;
        } catch (SQLException throwable) {
            throwable.printStackTrace();
            throw new DaoException(throwable);
        }
    }
    private static final ClientDAO INSTANCE = new ClientDAO();
    public static ClientDAO getInstance() {
        return INSTANCE;
    }
    private Client buildClient(ResultSet resultSet) throws SQLException {
        return new Client(
                resultSet.getInt("id"),
                resultSet.getString("legal_entity_name"),
                resultSet.getString("bank_details")
        );
    }
}

Вспомогательный класс DaoException

public class DaoException extends RuntimeException {
    public DaoException(Throwable throwable) {
        super(throwable);
    }
}

DBConnection

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBConnection {
    private static DBConnection instance = null;
    public static DBConnection getInstance() throws SQLException {
        if(null == instance)
            instance = new DBConnection();

        return instance;
    }
    public Connection getConnection() {
        Connection connection = null;
        try{
            connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/bd","login","pass");
        }catch(SQLException e){
            e.printStackTrace();
        }
        return connection;
    }
}

Результат результат работы

→ Ссылка