JavaFX - MVC 应用程序与数据库的最佳实践
JavaFX - MVC Application best practices with database
我是 JavaFX 的新手,我想知道用这种语言开发 MVC 数据库应用程序的最佳实践是什么,如果您是高级开发人员,我想我的问题会很简单。
让我们考虑一个用 JavaFX 开发的基本应用程序的简单示例:一个与 SQL 数据库链接的 ToDoList。
- 数据库只有一个 table Task,带有 id 和 taskDescr VARCHAR 字段。
- 目的很简单:我们只想在 TableView 或 ListView 中显示任务并能够添加一些任务。
这就是我们的应用程序的样子:
ToDoList GUI
我决定将我的代码分成四个部分,DAO 用于代表 table 中的数据的 classes(Task.java ),访问数据库的 DAO class(它的行为在这里无关紧要)。 model 代表我们 TodoList 的模型部分(包含任务列表并对其执行操作,调用 DAO 等)。 FXML Views 和 Controller :
Project structure
接下来,您可以找到我们感兴趣的不同 classes 的代码(我们假设 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 更新了我的数据库(a DAO 中的创建将在列表中添加之后执行)和最重要的:我应该在控制器的 handleAddButton 中执行哪些操作?在这里,我首先在我的 TodoListModel 中使用了 add 方法,但这还不够,因为我的可观察列表被错误地更新了(添加的任务出现了,但我们不能用鼠标 select 它)。然后,当我也将它添加到 TableView 项目中时,任务出现了两次并且已在列表中添加了两次。
因此,我了解到 ObservableList 已链接到我的 ToDoListModel 中的列表,但是如果我只想在我的模型中对该列表进行操作但要更新 ObservableList,我应该怎么做正确吗? (选择table 项等...)
Duplication example
提前感谢您的帮助和耐心等待,
真挚地,
保罗
这是一个示例实现
DAO
class 负责连接到数据库(可能使用池或其他东西)。在这种情况下,它建立了一个简单的连接。
public class DAO {
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://192.168.40.5:3306/test", "root", "");
}
}
ToDoListModel
class 通过使用 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 线程只能由它唯一地创建。
我是 JavaFX 的新手,我想知道用这种语言开发 MVC 数据库应用程序的最佳实践是什么,如果您是高级开发人员,我想我的问题会很简单。
让我们考虑一个用 JavaFX 开发的基本应用程序的简单示例:一个与 SQL 数据库链接的 ToDoList。
- 数据库只有一个 table Task,带有 id 和 taskDescr VARCHAR 字段。
- 目的很简单:我们只想在 TableView 或 ListView 中显示任务并能够添加一些任务。
这就是我们的应用程序的样子:
ToDoList GUI
我决定将我的代码分成四个部分,DAO 用于代表 table 中的数据的 classes(Task.java ),访问数据库的 DAO class(它的行为在这里无关紧要)。 model 代表我们 TodoList 的模型部分(包含任务列表并对其执行操作,调用 DAO 等)。 FXML Views 和 Controller :
Project structure
接下来,您可以找到我们感兴趣的不同 classes 的代码(我们假设 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 更新了我的数据库(a DAO 中的创建将在列表中添加之后执行)和最重要的:我应该在控制器的 handleAddButton 中执行哪些操作?在这里,我首先在我的 TodoListModel 中使用了 add 方法,但这还不够,因为我的可观察列表被错误地更新了(添加的任务出现了,但我们不能用鼠标 select 它)。然后,当我也将它添加到 TableView 项目中时,任务出现了两次并且已在列表中添加了两次。
因此,我了解到 ObservableList 已链接到我的 ToDoListModel 中的列表,但是如果我只想在我的模型中对该列表进行操作但要更新 ObservableList,我应该怎么做正确吗? (选择table 项等...)
Duplication example
提前感谢您的帮助和耐心等待, 真挚地, 保罗
这是一个示例实现
DAO
class 负责连接到数据库(可能使用池或其他东西)。在这种情况下,它建立了一个简单的连接。
public class DAO {
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://192.168.40.5:3306/test", "root", "");
}
}
ToDoListModel
class 通过使用 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 线程只能由它唯一地创建。