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

JavaFX-MVC应用程序与数据库的最佳实践,java,database,model-view-controller,javafx,fxml,Java,Database,Model View Controller,Javafx,Fxml,我是JavaFX新手,我想知道用这种语言开发MVC数据库应用程序的最佳实践是什么,如果你是一名高级开发人员,我想我的问题会很简单 让我们考虑一个在JavaFX中开发的基本应用程序的简单示例:链接到SQL数据库的ToLoister. 数据库只是一个带有id和taskDescr VARCHAR字段的表 目的非常简单:我们只想在TableView或ListView中显示任务,并能够添加一些任务 这就是我们的应用程序的外观: 我决定将代码分为四个部分,DAO用于表示表中数据的类(Task.java

我是JavaFX新手,我想知道用这种语言开发MVC数据库应用程序的最佳实践是什么,如果你是一名高级开发人员,我想我的问题会很简单

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

  • 数据库只是一个带有id和taskDescr VARCHAR字段的表
  • 目的非常简单:我们只想在TableView或ListView中显示任务,并能够添加一些任务
这就是我们的应用程序的外观:

我决定将代码分为四个部分,DAO用于表示表中数据的类(Task.java),即访问数据库的DAO类(其行为在这里并不重要)。模型代表TodoList的模型部分(包含任务列表,并对其执行操作,调用DAO等)。FXML视图控制器

接下来,您可以找到我们感兴趣的不同类的代码(我们假设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;
    }
}
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;
    }
}
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();
    }

}
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.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;
    }
}
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;
    }
}
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();
    }

}
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,我在同一个任务中更新了我的对象列表任务,我用DAO更新了我的数据库(DAO中的创建将在列表中的外接程序之后执行)最重要的是:我应该在控制器的handleAddButton中执行什么操作?这里我首先在TodoListModel中使用add方法,但这还不够,因为我的可观察列表被错误地更新(添加的任务出现了,但我们无法用鼠标选择它)。然后,当我也将其添加到TableView项目中时,该任务会出现两次,并且已在列表中添加了两次

因此,我知道ObservableList链接到我的ToDoListModel中的列表,但是如果我只想在我的模型中对该列表执行操作,但得到ObservableList的正确更新,我应该怎么做?(可选项等)

提前感谢您的帮助和耐心, 真诚地
Paul

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

public class DAO {
    public Connection getConnection() throws SQLException {
        return  DriverManager.getConnection("jdbc:mysql://192.168.40.5:3306/test", "root", "");
    }
}
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
类通过使用
DAO
的实例获取有效连接来处理数据库

public class DAO {
    public Connection getConnection() throws SQLException {
        return  DriverManager.getConnection("jdbc:mysql://192.168.40.5:3306/test", "root", "");
    }
}
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;
                }
            }
        }
    }
}

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

没有错误处理逻辑。在UI线程中使用数据库是不好的。
ToDoListModel
可以与
一起使用>ObservableList
,不需要保留其实例(她坐在控件中).
handleAddButton
可以使用
ToDoListModel
立即将元素保存到数据库中,但这在进行小更改时非常有用。当您进行更多编辑时,最好使用单独的保存按钮,只允许对数据库进行一次请求(
upsert
)保存整个列表和一个
delete
删除。您好,是的,正如我所说,我编写错误处理代码并不是为了简化此处发布的代码。好的,所以我必须用一个可观察列表替换TaskListModel中的列表,并对其进行操作?我想我不明白您的第二篇帖子:/好的,我明白您的意思,因此我理解了在这里,经典的“模型-视图-控制器”模式不能在JavaFX中实现,因为模型(通常携带所有数据并和和数据库交互的类DAO交互)是由控制器本身在JavaFX中表示的?我认为在繁重的应用程序中操纵大量数据(数据库数据和无数据库数据)我们使用了一个模型来管理所有思考并将其提供给控制器。“UI线程只能由它唯一地生成。”->我没有理解这一点:/?哦,好的,我理解了,你的意思是我的
this.Model.addTask(t)
更改了UI?我在之前的评论中是否正确理解了MVC原则?对于那些必须管理其模型中其他数据的大型应用程序呢?@PaulThirozier,是的,对您使用的数据(模型)的所有更改反映在用户界面中。因此,这些更改只应在应用程序线程中执行。在您的示例中,没有异步数据加载,因此无需担心(用户界面将冻结除外)。这是大型项目的方法。这里的模型是
任务
,如果您习惯了
Swing
体系结构,这可能会让您感到困惑(需要制作一个模型来保持数据收集)哦,是的,我认为我对这些不同的方法有点困惑。所以,我理解,如果我构建了一个沉重的JavaFX应用程序,我应该考虑异步数据加载的方法(我理解代码,谢谢你)。如果我需要处理不在数据库中的其他数据,我会为每种类型的数据创建另一个类。但是,为所有数据实现“ModelContainer”并不是一个好的做法?问题在于。MVC类型体系结构背后的驱动动机是使同一数据(模型)具有多个视图成为可能。如果从一个视图修改数据,则其他视图应该能够更新(当然,这是在模型不依赖于视图的情况下完成的)。您的设计不允许这样做:例如,想象一个相同数据的
列表视图
(可能在另一个视图中)