2016-12-08 3 views
2

Я пишу небольшое приложение JavaFx, где в главном классе хранится ObservableArryList пользователей. Эти пользователи имеют ObservableList дебиторов, и эти счета имеют ObservableList-х сделок и так далее ...Запись/чтение вложенных ObservableList из приложения JavaFX

Вот класс-схема: Class-Diagram

Я хотел бы сохранить, а затем прочитать данные приложения/fram файл.

Я уже пытаюсь сохранить его, внедряя Интерфейс Serializable во всех моих классах, но, судя по всему, вы не можете Сериализовать ObservableList.

Я также попытался сохранить его в Json-файле с помощью Gson или XML-файла с JAXB, но ни один из них не сохранил списки рекурсивно.

Так что мой вопрос: Кто-нибудь знает, как я могу сохранить все объекты, которые в настоящее время находятся в моем приложении, а затем загрузить их снова?

EDIT: Я реализовал подход JAXB на основе хранения, данное jewelsea и сохранение/загрузка данных теперь работает отлично.

ответ

2

Общая конструкция подход рекомендация

Для вашей проблемы, я был бы склонен использовать базу данных вместо сериализации. Есть много вариантов, в зависимости от ваших потребностей. Для небольшой встроенной базы данных, что-то вроде H2 было бы разумным выбором. Здесь приведен пример интеграции JavaFX and H2.

Для настойчивости вы можете использовать прямой JDBC или JPA. Для существенного применения JPA будет лучше. Для небольшого приложения JDBC достаточно. Если вы используете JPA, вы можете интегрировать его с классами на основе свойств JavaFX, как это определено в статьях, связанных с Put together JavaFX properties and JPA Entities (NO MIXED MODE) и этим JavaFX plus JPA example. Тем не менее, вы можете захотеть сохранить объекты свойств модели представления JavaFX отдельно и использовать DAO pattern для вашего сохранения. Сохранение отдельных объектов дает вам немного большую гибкость в разработке и реализации приложений, но нарушает принципы DRY. Это компромисс, хотя результирующие объекты лучше уважают single responsibility principle.

Определите отдельные таблицы для каждого из ваших объектов (пользователей, учетных записей, получателей, транзакций). Назначьте каждому элементу объекта уникальный ключ ключа. Используйте ссылку relations, чтобы связать ссылки на элементы, которые вы сохранили в своих наблюдаемых списках.

Если вы хотите получить доступ к базе данных из удаленных мест, и вы не можете открыть для нее прямое подключение к порту, вам необходимо будет предоставить услугу на сервере, который предоставляет данные (например, сервер на основе REST, который выполняет доступ к базе данных и предоставляет требуемые данные в виде JSON через HTTP, которые клиент JavaFX обращается через клиент REST, затем обрабатывает ответы ответа REST в структуры данных на основе свойств JavaFX клиента). Такие реализации быстро становятся очень полезными :-)

Возможно, я не должен был отвечать на этот вопрос, поскольку вопрос (или моя интерпретация его) слишком широк по принципам StackOverflow, но, надеюсь, информация здесь вам полезна ,

Конкретная ответ, основанный на дополнительной информации

Я на самом деле уже есть на основе пружинного загрузки веб-приложения с помощью DAO и Hibernate, что работает хорошо, и это JavaFX App планируется подключить к этому веб-приложение , Мне просто нужны эти локально сохраненные файлы в виде небольшой «демонстрации» программы, если в настоящее время нет доступного интернет-соединения

Gotcha, что делает полный смысл. Я уже включил JavaFX с SpringBoot, но, к сожалению, я не могу публиковать источник для этих реализаций публично.

Для вашей демонстрационной программы необходимо сохранить настойчивость через JAXB или Jackson. Makery - отличный пример для сохранения Java на базе JAXB.

Трюк с подходом JAXB заключается в том, чтобы получить что-то, что работает с вашей вложенной моделью данных.

JAXB на основе примеров подход хранения

Этот пример основан на идеях из Makery JavaFX tutorial. Чтобы лучше понять это, проконсультируйтесь с учебником. Вложенность наблюдаемого списка сохраняется с использованием понятий от: JAXB: How to marshal objects in lists?.

Ключом к решению является этот бит кода в классе User. Он предоставляет список учетных записей в виде вложенного ObservableList и предоставляет стандартный аксессуар accounts() для извлечения ObservableList в соответствии с соглашениями JavaFX. Он также предоставляет метод getAccounts() и setAccounts(), который копирует в ObservableList и выводит его из стандартного списка Java и уведомляет получателя с аннотациями JAXB @Xml..., чтобы позволить 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); 
} 

UserAccountPersistence.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(); 
    } 
} 

UserListWrapper.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; 
    } 
} 

User.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); 
    } 
} 

Выход

$ cat /Users/jewelsea/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> 
+0

Большое спасибо за быстрый ответ! На самом деле у меня уже есть веб-приложение с весной-загрузкой с DAO и Hibernate, которое отлично работает, и это приложение JavaFX планируется подключиться к этому веб-приложению. Мне просто нужны эти локально сохраненные файлы как небольшая «демонстрация» программы, если в настоящее время нет доступного интернет-соединения.Я обязательно рассмотрю примеры H2, которые вы предоставили! – UniqUnicorn

+0

Я обновил вопрос с помощью нового примера JAXB, который вы предоставили! – UniqUnicorn

+0

@UniqUnicorn Я не знаю, почему unmarshalling не работает для вашей версии кода. Я дважды проверил код в своем ответе, и unmarshalling работает для кода, который я предоставил. Я не знаю причины различия в поведении, которое вы испытываете для своей реализации. – jewelsea

Смежные вопросы