当前位置: 首页 > 知识库问答 >
问题:

从JavaFX应用程序写入/读取嵌套的观察列表

万俟沛
2023-03-14

我正在编写一个小型 JavaFx 应用程序,其中主类包含一个可观察的用户列表。这些用户具有可观察的帐户列表,并且这些帐户具有可观察的交易列表等等...

我想保存并稍后读取应用程序的数据以/构建文件。

我已经尝试通过在我的所有类中实现可序列化接口来保存它,但显然你不能序列化一个可观察列表。

我还尝试使用 Gson 将其保存在 Json 文件中,或者使用 JAXB 将其另存为 XML 文件,但是它们都没有递归地存储列表。

所以我的问题是:有人知道如何保存应用程序中当前的所有对象,然后再重新加载它们吗?

编辑:我实现了jewelsea提供的基于JAXB的存储方法,数据的保存/加载现在工作得很好。

共有1个答案

羊舌高爽
2023-03-14

一般设计方法建议

对于你的问题,我倾向于使用数据库而不是序列化。根据你的需要,有很多选择。对于小型嵌入式数据库,像H2这样的东西是一个合理的选择。这里提供了一个集成JavaFX和H2的例子。

对于持久性,可以直接使用JDBC或JPA。对于实际应用,JPA会更好。对于小型应用程序,JDBC就足够了。如果您使用JPA,您可以将其与基于JavaFX属性的类集成,如链接到Putto together JavaFX properties and JPA Entities(NO MIXED MODE)和这个JavaFX plus JPA示例的文章中所定义的那样。然而,您可能希望将JavaFX视图模型属性对象分开,并使用DAO模式进行持久化。保持对象分离可以使应用程序的设计和实现更加灵活,但违反了DRY原则。但这是一种权衡,因为结果对象更好地尊重单一责任原则。

为每个实体(用户、帐户、收件人、交易)定义单独的表。为每个实体条目分配一个唯一的id键。使用关系链接存储在观察列表中的项目引用。

如果您想从远程位置访问数据库,但无法打开到该数据库的直接端口连接,然后,您需要在提供数据的服务器上提供服务(例如,基于REST的服务器,它执行数据库访问,并通过HTTP将所需数据作为JSON公开,JavaFX客户端通过REST客户端访问这些数据,然后将REST调用响应处理到客户端基于JavaFX属性的数据结构中)。这样的实现很快就需要做很多工作:-)

也许我不应该回答这个问题,因为根据StackOverflow原则,这个问题(或我对它的解释)太宽泛了,但希望这里的信息对你有用。

基于其他信息的具体答案

实际上,我已经有一个基于Spring boot的Web应用程序,带有DAO和Hibernate,运行良好,并且这个JavaFX应用程序计划连接到那个Web应用程序。如果目前没有可用的互联网连接,我只需要这个本地保存的文件作为程序的一个小“演示”

明白了,这完全有道理。我以前将JavaFX与SpringBoot集成过,但不幸的是,我不能公开发布这些实现的源代码。

对于您的演示程序,通过JAXB或杰克逊的持久性应该就足够了。创客为基于 JAXB 的 JavaFX 持久性提供了一个很好的例子。

基于JAXB的方法的诀窍是获得一些与嵌套数据模型一起工作的东西。

基于JAXB的存储方法示例

此示例基于Makery JavaFX教程中的想法。为了更好地理解它,请参考该教程。嵌套的可观察列表持久性是使用以下概念实现的:JAXB:如何在列表中列出对象?。

解决方案的关键是 User 类中的这一段代码。它将帐户列表作为嵌套的可观察列表提供,并提供一个标准访问器帐户 () 来根据 JavaFX 约定检索可观察列表。它还提供了一个 getAccounts()setAccounts() 方法,该方法将可观察列表的传入和传出复制到标准的 Java 列表中,并使用 JAXB @Xml... 注释来标记 getter,以使 JAXB 能够处理链接到用户的帐户的序列化和反序列化。

private final ObservableList<Account> accounts = FXCollections.observableArrayList();

public ObservableList<Account> accounts() { return accounts; }

@XmlElementWrapper(name="accounts")
@XmlElement(name = "account")
public List<Account> getAccounts() {
    return new ArrayList<>(accounts);
}

public void setAccounts(List<Account> accounts) {
    this.accounts.setAll(accounts);
}

用户帐户持久性.java

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;

public class UserAccountPersistence {

    private ObservableList<User> users = FXCollections.observableArrayList();

    public UserAccountPersistence() throws JAXBException, IOException {
        File dbFile = getDatabaseFilePath();
        if (dbFile == null) {
            setDatabaseFilePath(new File(System.getProperty("user.home") + "/" + "user-account.xml"));
            dbFile = getDatabaseFilePath();
        }

        if (!dbFile.exists()) {
            createTestData();
            saveData(dbFile);
        } else {
            loadData(dbFile);
        }

        System.out.println("Persisted Data: ");
        System.out.println(
                Files.lines(dbFile.toPath())
                        .collect(Collectors.joining("\n"))
        );
        System.out.println("Database File: " + dbFile);
    }

    private void createTestData() {
        users.add(new User("Hans", "Muster"));
        users.add(new User("Ruth", "Mueller"));
        users.add(new User("Heinz", "Kurz"));

        users.get(0).accounts().addAll(
                new Account(10),
                new Account(20)
        );

        users.get(2).accounts().addAll(
                new Account(15)
        );
    }

    public File getDatabaseFilePath() {
        Preferences prefs = Preferences.userNodeForPackage(UserAccountPersistence.class);
        String filePath = prefs.get("filePath", null);
        if (filePath != null) {
            return new File(filePath);
        } else {
            return null;
        }
    }

    public void setDatabaseFilePath(File file) {
        Preferences prefs = Preferences.userNodeForPackage(UserAccountPersistence.class);
        if (file != null) {
            prefs.put("filePath", file.getPath());
        } else {
            prefs.remove("filePath");
        }
    }

    public void loadData(File file) throws JAXBException {
        JAXBContext context = JAXBContext
                .newInstance(UserListWrapper.class);
        Unmarshaller um = context.createUnmarshaller();

        UserListWrapper wrapper = (UserListWrapper) um.unmarshal(file);

        users.clear();
        users.addAll(wrapper.getPersons());

        setDatabaseFilePath(file);
    }

    public void saveData(File file) throws JAXBException {
        JAXBContext context = JAXBContext
                .newInstance(UserListWrapper.class);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        UserListWrapper wrapper = new UserListWrapper();
        wrapper.setPersons(users);

        m.marshal(wrapper, file);

        setDatabaseFilePath(file);
    }

    public static void main(String[] args) throws JAXBException, IOException {
        UserAccountPersistence userAccountPersistence = new UserAccountPersistence();
    }
}

用户列表包装.java

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "users")
public class UserListWrapper {

    private List<User> persons;

    @XmlElement(name = "user")
    public List<User> getPersons() {
        return persons;
    }

    public void setPersons(List<User> persons) {
        this.persons = persons;
    }
}

用户.java

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class User {
    private final StringProperty id;
    private final StringProperty firstName;
    private final StringProperty lastName;

    private final ObservableList<Account> accounts = FXCollections.observableArrayList();

    public User() {
        this(UUID.randomUUID().toString(), null, null);
    }

    public User(String firstName, String lastName) {
        this(UUID.randomUUID().toString(), firstName, lastName);
    }

    public User(String id, String firstName, String lastName) {
        this.id = new SimpleStringProperty(id);
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);
    }

    public String getId() {
        return id.get();
    }

    public void setId(String id) {
        this.id.set(id);
    }

    public StringProperty idProperty() {
        return id;
    }

    public String getFirstName() {
        return firstName.get();
    }

    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }

    public StringProperty firstNameProperty() {
        return firstName;
    }

    public String getLastName() {
        return lastName.get();
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }

    public StringProperty lastNameProperty() {
        return lastName;
    }

    public ObservableList<Account> accounts() { return accounts; }

    @XmlElementWrapper(name="accounts")
    @XmlElement(name = "account")
    public List<Account> getAccounts() {
        return new ArrayList<>(accounts);
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts.setAll(accounts);
    }

}

Account.java

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

import java.util.UUID;

public class Account {
    private final StringProperty id;
    private final IntegerProperty balance;

    public Account() {
        this(UUID.randomUUID().toString(), 0);
    }

    public Account(int balance) {
        this(UUID.randomUUID().toString(), balance);
    }

    public Account(String id, int balance) {
        this.id = new SimpleStringProperty(id);
        this.balance = new SimpleIntegerProperty(balance);
    }

    public String getId() {
        return id.get();
    }

    public void setId(String id) {
        this.id.set(id);
    }

    public StringProperty idProperty() {
        return id;
    }

    public int getBalance() {
        return balance.get();
    }

    public IntegerProperty balanceProperty() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance.set(balance);
    }
}

输出

< code > $ cat/Users/jewel sea/user-account . XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<users>
    <user>
        <accounts>
            <account>
                <balance>10</balance>
                <id>a17b8244-5d3a-4fb4-a992-da26f4e14917</id>
            </account>
            <account>
                <balance>20</balance>
                <id>f0b23df5-3cc0-418c-9840-633bc0f0b3ca</id>
            </account>
        </accounts>
        <firstName>Hans</firstName>
        <id>078dad74-ea9d-407d-9be5-d36c52c53b0d</id>
        <lastName>Muster</lastName>
    </user>
    <user>
        <accounts/>
        <firstName>Ruth</firstName>
        <id>78513f1b-75ee-4ca9-a6f0-444f517e3377</id>
        <lastName>Mueller</lastName>
    </user>
    <user>
        <accounts>
            <account>
                <balance>15</balance>
                <id>77c4fd3c-5f7a-46cf-a806-da1e6f93baab</id>
            </account>
        </accounts>
        <firstName>Heinz</firstName>
        <id>651d9206-42a5-4b76-b89e-be46dce8df74</id>
        <lastName>Kurz</lastName>
    </user>
</users>
 类似资料:
  • 我正在尝试用来自可观察列表的数据填充TableView。我以前做过这个,但由于某种原因我现在无法让它工作。我没有得到任何异常或任何东西,但它根本不会向表添加任何东西。 这个问题很相似,但我发现了JPA引起的另一个问题,这使得提到的的构造函数永远不会执行。相反,JPA会神奇地分配值。 这是我的代码-我剪下了与问题无关的代码: FXML 主要的Java语言 修改Java语言 任何帮助解决这个问题将不胜

  • 我对Kafka和Kafka流很陌生,所以请容忍我。我想知道我是否在正确的轨道上。 我正在给一个Kafka主题写信,试图通过rest服务访问数据。在访问原始数据之前,需要对其进行转换。 到目前为止,我拥有的是一个将原始数据写入主题的制作人。 1)现在我想要streams应用程序(应该是一个在容器中运行的jar),它可以将数据转换为我想要的形状。遵循这里的物化视图范式。 1的过度简化版本。) 2)和另

  • #include <stdio.h> #include <pthread.h> int a = 0; void *thread1_func(void *p_arg) { while (1) { a++; sleep(10); } } void *thread2_func(void

  • 我正在用一个库编程,我不知道代码,只知道方法,我不能修改它。我试着制作一个“航班”的表格视图,但我不知道如何为每个航班命名(或ID)。有人能帮我吗?谢谢此处有一些代码:

  • 我有一个很大的swing应用程序,我想把javafx嵌入其中。我多次尝试这样做(通过遵循oracle教程等),但只有在声明一个新的JFrame以使用JFXPanel组件时才成功。但是,我不想使用新的框架,我想将我的Javafx代码合并到swing应用程序的根JFrame中。 我们可以将javaFX组件嵌入到JPanel而不是JFrame中吗?如果答案是肯定的,为什么我没有成功?

  • 我试图从数据(ObservableList)中填充tableview2(fx:id tableview)行。但当它完成时,tableview2只显示空记录。在控制台中,我看到数据中的所有行(ObservableList)。我知道stackoverflow中有很多关于这方面的问题,很抱歉(还有我的英语),但请你问我代码中的错误在哪里?感谢您的帮助或关注)。 更新(添加MainPayer.java和更