我正在编写一个小型 JavaFx 应用程序,其中主类包含一个可观察的用户列表。这些用户具有可观察的帐户列表,并且这些帐户具有可观察的交易列表等等...
我想保存并稍后读取应用程序的数据以/构建文件。
我已经尝试通过在我的所有类中实现可序列化接口来保存它,但显然你不能序列化一个可观察列表。
我还尝试使用 Gson 将其保存在 Json 文件中,或者使用 JAXB 将其另存为 XML 文件,但是它们都没有递归地存储列表。
所以我的问题是:有人知道如何保存应用程序中当前的所有对象,然后再重新加载它们吗?
编辑:我实现了jewelsea提供的基于JAXB的存储方法,数据的保存/加载现在工作得很好。
一般设计方法建议
对于你的问题,我倾向于使用数据库而不是序列化。根据你的需要,有很多选择。对于小型嵌入式数据库,像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和更