【问题标题】:JavaFx Controller Initialization Order - Database Called Multiple timesJavaFx 控制器初始化顺序 - 多次调用数据库
【发布时间】:2017-12-18 17:54:04
【问题描述】:

我有一个扩展应用程序的标准 JavaFX 应用程序。我展示的第一页是一个仪表板,带有用于打开其他应用程序的按钮。没关系,第一个应用程序是一个名为 AdminController 的管理页面,它允许用户对模型对象进行 CRUD 操作 - 用户、联系人、产品每个实体都是管理视图上的选项卡 - 所以在我的主 Admin FXML(使用 SceneBuilder ) 我使用 fx 包含其他 fxml 页面:包括用户选项卡、联系人选项卡和产品选项卡。同样,这些包含都在具有自己的控制器的 Admin.fxml 中。

<Tab fx:id="contactsTab" onSelectionChanged="#goToContacts" text="Contact">
   <content>
     <fx:include source="/fxml/manager_contacts_tab.fxml" fx:id="contact" />
   </content
<Tab/>

我的想法是在 AdminController 中加载每个页面需要的资源、用户/联系人/产品,这样我使用的每个 fx:include 都可以使用这些相同的对象。由于某些选项卡需要所有这些资源,例如用户来管理关联,而其他选项卡(例如产品)只需要产品列表。我的主控制器看起来像这样 我

public class AdminController {
  @FXML
  UserController userController;
  @FXML
  ProductController productController;
  @FXML
  ContactController contactController;

@FXML
public void initialize
    // at this point userController already ran, and called DB so it calls it again here
    if (users == null) {
        this.users = FXCollections.observableArrayList(userDao.getAllWithProductsAndContacts());
    }
    if (products == null) {
        this.products = FXCollections.observableArrayList(productDao.getAll());
    }
    if (contacts == null) {
        this.contacts = FXCollections.observableArrayList(contactDao.getAll());
    }
    contactController.setContacts(contact);
    // set other resources, like products
}

从逻辑上讲,我认为 AdminController 的初始化方法将首先运行,这将允许我在子控制器中设置我需要的任何对象,因为我可以访问它们,但发生的是子控制器运行首先,例如。与联系人控制器

public class ContactController {
    @FXML
    public void initialize() {
        if (contacts == null) {
            this.contacts = FXCollections.observableArrayList(contactDao.getAll());
        }
    }

所以我的应用程序初始化方法按照它们包含在 admin.fxml userController、contactController、productController 中的顺序运行

我认为我的问题归结为如何在子控制器之间共享对象,以便当单击具有与前一个相同的资源列表的新选项卡时,不会启动新的数据库调用 - 在我的示例中当我加载用户页面时会发生这种情况,它需要联系人列表和产品列表,但 ContactController 和 ProductController 需要相同的列表,因此不需要进行另一个数据库调用。

【问题讨论】:

  • 对我来说,您在这里要问什么并不是 100% 清楚的。数据库调用是什么意思?通常,您只查询一个数据库(除非您正在修改它),并且只需要 ms 即可完成。否则(如果数据库足够小),您可以在程序启动时将其加载到内存中,而根本不会在控制器中获取它。您所说的数据库是指您序列化的一堆文件还是您在谈论 SQL 数据库?
  • 我通常用小存储做的是定义一个名为“Data”的类,并在其中存储包含数据库对象(如用户、产品等)的 ArrayList。我在程序启动时加载这些 ArrayList。这样你的控制器就可以调用像 Data.getContacts(); 这样的东西。抓住所有的联系人。如果我们谈论的是一个巨大的数据库,那么应该运行 SQL 查询来获取信息。
  • 抱歉应该已经澄清了 - 它是一个 SQL 数据库,我认为我的问题不是首先调用数据库以获取我正在使用的项目列表 - 我的第一个想法是做所有AdminController 中的数据库调用以设置 ChildController,但子控制器在 AdminController 之前被初始化,因此此时需要进行调用,并且共享相同数据列表的子控制器彼此不可见,例如 Contact /Admin Controllers - ContactController 需要联系人,但 UserController 也需要联系人(显示两者之间的关联)。

标签: java javafx controller scenebuilder


【解决方案1】:

在您的情况下,如果您一次加载“所有”联系人,我可能会在初始化单个控制器之前调用数据库。

package main;

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

    public void start(Stage window) throws Exception {
        // Database call goes here!
        DatabaseManager.initialize();


        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        // Whatever window initialization stuff you need to do
        window.show();
    }

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

}

现在是你的 DatabaseManager 类

package sample;

public class DatabaseManager {

    public static void initialize() {
        // Query the database for your information.
        // Little pseudo code here
        Data.setUsers(Database.userQuery);
        Data.setContacts(Database.contactQuery);
        Data.setProducts(Database.productQuery);
    }

}

现在是数据类

package sample;

public class Data {

    public static ArrayList<User> getUsers() {
        return users;
    }

    public static void setUsers(ArrayList<User> users) {
        this.users = users;
    }

    public static ArrayList<Contact> getContacts() {
        return contacts;
    }

    public static void setContacts(ArrayList<Contact> contacts) {
        this.contacts = contacts;
    }

    public static ArrayList<Product> getProducts() {
        return products;
    }

    public static void setProducts(ArrayList<Product> products) {
        this.products = products;
    }

    private static ArrayList<User> users = new ArrayList<User>();
    private static ArrayList<Contact> contacts = new ArrayList<Contact>();
    private static ArrayList<Product> products = new ArrayList<Product>();

}

现在你的控制器只需要调用

this.contacts = FXCollections.observableArrayList(Data.getContacts());

这将使您的联系人数组列表准备就绪。

另请注意,这就是所谓的“将其加载到内存中”。换句话说,您将所有这些信息存储在计算机的内存中并从那里访问它。这不适用于庞大的数据库,因为您的计算机没有大脑空间。无论如何,希望这会有所帮助。

【讨论】:

  • 谢谢!现在我的应用程序足够小,可以在其中工作 - 但是计划它将成长为一个大型应用程序,您对更大的应用程序有什么建议吗?理想情况下,我只想在必要时加载它,但是控制器 @FXML 初始化让我感到困惑,并且没有按照我期望的顺序运行。我的期望 -> AdminController - 加载视图所需的内容,以便与此控制器相关的视图,即 ContactController 可以访问 AdminControllers 联系人 - 只有 ContactController 首先初始化。
  • 老实说,我从不喜欢控制器的“初始化”方法。这对我来说也有点奇怪。我不确定您的程序是如何运行的,但对我来说,我在所有 FX 项目中所做的是创建一个“控制器”类并从该类扩展我的所有其他控制器,并且我有一个公共 start() 方法我在那个父类中组成了自己,每当我加载控制器时,我都会调用 start() 方法。所有扩展控制器都会覆盖 start() 方法。
  • 谢谢你,菲利普,我很感激你的答案——我会再摆弄一下,看看我能想出什么。
  • 又摆弄了一些,你为什么要打扰,我采用了你的抽象控制器方式,它解决了我的问题,干杯。
猜你喜欢
  • 1970-01-01
  • 2020-09-05
  • 1970-01-01
  • 2013-11-12
  • 2019-07-22
  • 2020-12-13
  • 1970-01-01
  • 2016-04-19
相关资源
最近更新 更多