java - 在填充ObservableList时,是否必须从数据库中加载所有记录?

标签 java javafx

因此,我正在将我的Swing Java数据库应用程序移植到Java FX(这里还是一个初学者,最近我刚刚学习了FXML和MVC模式的基础知识,请耐心等待)。

我打算将现有数据库中的数据加载到“学生” ObservableList中,以便可以在TableView上显示它,但是在我的原始Swing代码中,我有一个搜索TextField,当用户单击按钮或按Enter时,该程序:

  • 执行一个SQLite命令,该命令搜索特定记录并检索RecordSet。
  • 根据RecordSet内容创建一个DefaultTableModel
  • 并将该TableModel抛出到JTable中。

  • 但是,Java FX是完全不同的野兽(或者至少在我看来如此-别误会,我爱Java FX:D),所以我不确定该怎么做。

    因此,我的问题是,我是否必须将所有学生加载到数据库中,然后使用一些Java代码来筛选出不符合搜索条件的学生(并在搜索文本为空白时显示所有学生),或者我仍然使用SQLite过滤和检索记录(这意味着我需要清除列表,然后在每次执行搜索时添加学生,也许它还会弄乱绑定吗?也许这种方法也会带来速度上的损失?除此之外,它还会重置当前选择的记录,因为我清除了该列表-基本上,不良的UI设计会对可用性造成负面影响)

    根据正确的方法,还有一个后续问题(对不起,即使在谷歌搜索之后,我也确实找不到答案):
  • 如果我从数据库中获得所有学生,并使用Java实现搜索功能,它是否会消耗更多的RAM,因为我将所有数据库数据都存储在RAM中,而不仅仅是搜索的数据了吗?我的意思是,可以肯定的是,即使我的低端笔记本电脑也具有4GB RAM,但是使用比我应该多的内存的感觉让我感到有点内L LOL
  • 如果我选择在每次执行新搜索时仅更新ObservableList的内容,它会弄乱绑定吗?我是否需要重新设置绑定?如何在添加新内容之前清除ObservableList的内容?

  • 我还有一个想法,就是将所选表项设置为与搜索字符串匹配的第一条记录,但是我认为这将很难使用,因为每次搜索只能突出显示一条记录。即使我们突出显示多行,也很难浏览所有选定的项目。

    请给我正确的方法,而不是“简单”的方法。这是我第一次实现模式(我不知道是MVC还是我实际上是在做MVP),我意识到以前的程序是多么难以维护和丑陋,因为我使用了自己的样式。这是一个相对较大的项目,我需要支持和改进几年,因此拥有清晰的代码并以正确的方式进行操作应该有助于维护该程序的功能。

    在此先非常感谢您的帮助,希望我不要成为“甚至连Google都无法使用的傻瓜”问这些问题。请在这里忍受我。

    最佳答案

    基本设计权衡

    当然,您可以使用您描述的任何一种方式来执行此操作。基本的权衡是:

  • 如果您从数据库加载所有内容,并用Java过滤表,则会使用更多的内存(尽管不如您想象的那样多,如下所述)。
  • 如果您从数据库进行过滤并在每次用户更改过滤器时重新加载,则在显示数据时会有更大的延迟(延迟),因为将在数据库上执行新查询,并且(通常)数据库和应用程序是最大的瓶颈(尽管还有其他瓶颈)。

  • 数据库访问和并发

    通常,您应该在后台线程上执行数据库查询(请参见Using threads to make database requests)。如果您经常进行数据库查询(即通过数据库进行过滤),则会变得很复杂,并且需要在后台任务运行时频繁禁用UI中的控件。
    TableView设计和内存管理

    JavaFX TableView是一个虚拟控件。这意味着只为可见元素创建视觉组件(单元)(可能还有少量缓存)。然后,当用户滚动时,这些单元格将被重用,并根据需要显示不同的“项目”。视觉组件通常消耗大量内存(它们具有数百个属性-颜色,字体属性,尺寸,布局属性等-大多数具有CSS表示形式),因此限制创建的数量可以节省大量内存,并且无论表的后备列表中有多少项,表视图可见部分的内存消耗基本上是恒定的。

    常规内存消耗计算

    构成表的后备列表的items可观察列表仅包含数据:不难估计给定大小的列表消耗的内存量。 String每个字符使用2个字节,外加少量固定开销,double使用8个字节,int使用4个字节,依此类推。如果将字段包装在JavaFX属性中(推荐),则将有几个字节的开销每;每个对象的开销约为16个字节,引用本身通常最多使用8个字节。因此,存储几个字符串字段的典型Student对象通常会占用内存中的几百个字节。 (例如,当然,如果每个人都有一个与之关联的图像,则可能更多。)因此,如果从数据库中加载例如100,000个学生,则将消耗10-100MB的RAM,在大多数个人计算机系统上,这非常易于管理。

    粗略的一般准则

    因此,通常,对于您描述的那种应用程序,我建议您加载数据库中的内容并将其过滤到内存中。在我通常的工作领域(基因组学)中,有时我们需要数以十万计的实体,这是无法完成的。 (如果您的数据库包含all registered students in public schools in the USA,则可能会遇到类似的问题。)

    但是,作为一般经验法则,对于“普通”对象(即没有大数据对象(例如与之相关的图像)的对象),表的大小将过大,以致用户无法舒适地进行管理(即使使用过滤),然后再认真扩展用户计算机的内存容量。

    用Java过滤表(内存中的所有对象)

    代码过滤非常简单。简而言之,将所有内容加载到ObservableList中,然后将ObservableList包装在 FilteredList 中。 FilteredList包装了一个源列表,而Predicate则返回true,这是一个应该通过过滤器(包括在内)的项目;如果不包括在内,则返回false。

    因此,您将使用的代码段如下所示:
    ObservableList<Student> allStudents = loadStudentsFromDatabase();
    FilteredList<Student> filteredStudents = new FilteredList<>(allStudents);
    studentTable.setItems(filteredStudents);
    

    然后,您可以使用类似以下代码的文本字段来修改谓词:
    filterTextField.textProperty().addListener((obs, oldText, newText) -> {
        if (newText.isEmpty()) {
            // no filtering:
            filteredStudents.setPredicate(student -> true);
        } else {
            filteredStudents.setPredicate(student -> 
                // whatever logic you need:
                student.getFirstName().contains(newText) || student.getLastName().contains(newText));
        }
    });
    

    This tutorial对过滤(和排序)表有更彻底的处理。

    关于实现“通过查询过滤”的评论

    如果您不想从数据库中加载所有内容,则可以完全跳过过滤列表。查询数据库几乎肯定不会足够快地进行操作以根据用户类型进行过滤(使用新的数据库查询),因此您将需要一个“更新”按钮(或文本字段上的操作侦听器)来重新计算新的过滤数据。您可能还需要在后台线程中执行此操作。您无需在表的列上设置新的cellValueFactory(或cellFactory)或重新加载列;您只需在数据库查询完成后调用studentTable.setItems(newListOfStudents);

    关于java - 在填充ObservableList时,是否必须从数据库中加载所有记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44801274/

    相关文章:

    java - 从数组填充 GridView

    JavaFx 和内存消耗

    JavaFX 错误 : Class file has wrong version 54, 应该是 52

    java - 在 JavaFX 8 中重新对齐 AnchorPane 中的组件

    java - 如何将 java.awt.geom.AffineTransform 的实例转换为 javafx.scene.transform.Affine 的实例

    java - 拖动文本区域

    java - 矩阵到 JTable

    java - 将 EditText 保存在 bundle 中并在布局更改时恢复它

    java - 如何在Java中的不同子对象之间放置不同的父对象中的静态变量

    java - 双击时调整 JavaFX 选项卡的大小