JavaFX - MVC应用程序与数据库的最佳实践

时间:2018-01-16 08:48:32

标签: java database model-view-controller javafx fxml

我是JavaFX的新手,我想知道用这种语言开发MVC数据库应用程序的最佳做法是什么,我认为如果你是一名高级开发人员,我的问题将非常简单。

让我们考虑一个在JavaFX中开发的基本应用程序的简单示例:与SQL数据库链接的ToDoList。

  • 数据库只是一个表任务,其中包含 id taskDescr VARCHAR字段。
  • 目的很简单:我们只想在TableView或ListView中显示任务,并能够添加一些任务。

这就是我们的应用程序的样子:

ToDoList GUI

我决定将我的代码分为四个部分, DAO 用于表示表中的数据的类(Task.java),访问数据库的DAO类(其行为在此无关紧要) )。代表我们TodoList的Model部分的模型(包含任务列表和执行操作,调用DAO等)。 FXML 视图控制器

Project structure

接下来,你可以找到我们感兴趣的不同类的代码(我们认为DAO是正常的(自动设置id)我们不处理错误情况以简化代码:

Task.java

public class Task {

    private int id;
    private SimpleStringProperty task;

    public Task(int i, String s){
        this.id = i;
        this.task = new SimpleStringProperty(s);
    }

    public void setId(int i){
        this.id = i;
    }

    public int getId() {
        return id;
    }

    public String getTask() {
        return task.get();
    }

    public void setTask(String task) {
        this.task.set(task);
    }

    @Override
    public boolean equals(Object o){
        if(this.id == ((Task)o).id)
            return true;
        return false;
    }
}

ToDoListModel.java

public class ToDoListModel {

    private List<Task> taskList;
    private DAO dao;

    public ToDoListModel(){
        this.taskList = new ArrayList<Task>();
        this.dao = new DAO();
    }

    public void loadDatabase(){
        this.taskList = this.dao.getAllTasks();
    }

    public void addTask(Task t){
        // Operations throwing Exceptions such as : Does the task t is already in the list, etc...
        this.taskList.add(t);
        this.dao.createTask(t);
    }

    public void deleteTask(Task t){
        this.taskList.remove(t);
        this.dao.deleteTask(t);
    }

    public List<Task> getTaskList() {
        return taskList;
    }
}

Controller.java

public class Controller {

    private final ToDoListModel model;

    @FXML
    private TableView<Task> taskTable;
    @FXML
    private TableColumn<Task, String> taskColumn;
    @FXML
    private TextField taskTextField;

    public Controller(ToDoListModel m){
        this.model = m;
    }

    @FXML
    protected void initialize() {
        this.model.loadDatabase();

        // Setting up data table
        taskColumn.setCellValueFactory(new PropertyValueFactory<Task, String>("task"));
        ObservableList<Task> taskObservableList = FXCollections.observableList(this.model.getTaskList());
        taskTable.setItems(taskObservableList);
    }

    @FXML
    public void handleAddButton(ActionEvent e) {
        Task t = new Task(-1, this.taskTextField.getText());

        // What operations to do here ?
        this.model.addTask(t);
        this.taskTable.getItems().add(t);
        this.taskTable.refresh();
    }

}

Main.java

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        ToDoListModel model = new ToDoListModel();
        primaryStage.setTitle("My Todo");
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("views/View.fxml"));
        loader.setController(new Controller(model));
        Parent root = loader.load();
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

最后,我的问题是:我的做法好吗?我的意思是我已经创建了一个带有任务列表的ToDoListModel这一事实,我在同一任务中更新了对象列表 Task 我用DAO更新了我的数据库(创建在DAO中将在列表中添加后执行)并且最重要的是:我应该在Controller的 handleAddButton 中执行哪些操作?这里我首先使用了我的TodoListModel中的add方法但是它还不够,因为我的可观察列表被错误地更新了(添加的任务出现但我们不能用鼠标选择它)。然后,当我在TableView项目中添加它时,任务出现两次,并在列表中添加了两次。

结果我已经知道ObservableList链接到我在ToDoListModel中的List,但是如果我只想在我的模型中对该列表进行操作但是更新ObservableList,我该怎么办?对吗? (可选项目等......)

Duplication example

提前感谢您的帮助和耐心, 此致 保罗

1 个答案:

答案 0 :(得分:1)

这是一个示例实现
DAO类负责连接数据库(可以使用池或其他东西)。在这种情况下,它建立了一个简单的连接。

public class DAO {
    public Connection getConnection() throws SQLException {
        return  DriverManager.getConnection("jdbc:mysql://192.168.40.5:3306/test", "root", "");
    }
}

ToDoListModel类通过使用DAO实例来处理数据库,以获得有效的连接。

public class ToDoListModel {
    private DAO dao;

    public static ToDoListModel getInstance() {
        ToDoListModel model = new ToDoListModel();
        model.dao = new DAO();

        return model;
    }

    private ToDoListModel() {
    }

    public void addTask(Task task) throws SQLException {
        try(Connection connection = dao.getConnection()) {
            String q = "insert into todo (name) values (?)";

            try(PreparedStatement statement = connection.prepareStatement(q, Statement.RETURN_GENERATED_KEYS)) {
                statement.setString(1, task.getName());
                statement.executeUpdate();

                try(ResultSet rs = statement.getGeneratedKeys()) {
                    if(rs.next()) {
                        task.setId(rs.getInt(1));
                    }
                }
            }
        }
    }

    public void deleteTask(Task task) throws SQLException {
        try(Connection connection = dao.getConnection()) {
            String q = "delete from todo where id = ?";

            try(PreparedStatement statement = connection.prepareStatement(q)) {
                statement.setInt(1, task.getId());
                statement.executeUpdate();
            }
        }
    }

    public ObservableList<Task> getTaskList() throws SQLException {
        try(Connection connection = dao.getConnection()) {
            String q = "select * from todo";

            try(Statement statement = connection.createStatement()) {
                try(ResultSet rs = statement.executeQuery(q)) {
                    ObservableList<Task> tasks = FXCollections.observableArrayList();

                    while (rs.next()) {
                        Task task = new Task();
                        task.setId(rs.getInt("id"));
                        task.setName(rs.getString("name"));

                        tasks.add(task);
                    }

                    return tasks;
                }
            }
        }
    }
}

控制器使用ToDoListModel初始化TableView控件并添加操作(编辑和阅读 - 我没有实现它们,因为我坚持使用您的代码)

public class Controller {

    @FXML
    private TextField textField;

    @FXML
    private TableView<Task> tableView;

    @FXML
    private TableColumn<Task, String> nameTableColumn;

    @FXML
    private Button addButton;

    @FXML
    private void initialize() {
        nameTableColumn.setCellValueFactory(cdf -> cdf.getValue().nameProperty());

        addButton.disableProperty().bind(Bindings.isEmpty(textField.textProperty()));

        CompletableFuture.supplyAsync(this::loadAll)
            .thenAccept(list -> Platform.runLater(() -> tableView.getItems().setAll(list)))
            .exceptionally(this::errorHandle);
    }

    @FXML
    private void handleAddButton(ActionEvent event) {
        CompletableFuture.supplyAsync(this::addTask)
                .thenAccept(task -> Platform.runLater(() -> {
                    tableView.getItems().add(task);

                    textField.clear();
                    textField.requestFocus();
                }))
                .exceptionally(this::errorHandle);
    }

    private Task addTask() {
        try {
            Task task = new Task(textField.getText());
            ToDoListModel.getInstance().addTask(task);

            return task;
        }
        catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private ObservableList<Task> loadAll() {
        try {
            return ToDoListModel.getInstance().getTaskList();
        }
        catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private Void errorHandle(Throwable throwable) {
        throwable.printStackTrace();
        return null;
    }
}

任何数据库操作都与CompletableFuture异步,但您可以使用您喜欢的任何内容。重要的是要记住UI线程只能由它做出唯一的。