2016-12-08 7 views
2

メインJavaクラスにはObservableArryList of Usersが格納されている小さなJavaFxアプリケーションを作成しています。これらのユーザーはObservableListのアカウントのを持っており、それらのアカウントはObservableListの取引のを持っているように...ここでJavaFXアプリケーションからネストされたObservableListの書き込み/読み取り

は、クラス図である:Class-Diagram

私は保存して、後でへのアプリケーションのデータを読みたいです/フレーズファイル。

Serializableインターフェイスをすべてのクラスに実装することで既に保存しようとしていますが、明らかにObservableListをシリアル化できません。

Gsonを使用してJsonファイルに保存しようとしましたが、JAXBを使用してXMLファイルとして保存しようとしましたが、Listを再帰的に保存しませんでした。

私の質問は: 誰かが私のアプリケーションに現在あるすべてのオブジェクトを保存する方法を知っていますか?

EDIT: 私はjewelseaによって与えられたJAXBベースのストレージ・アプローチを実装し、データのセーブ/ロードは今完璧に働いています。あなたの問題のために

答えて

2

一般的な設計アプローチを推奨

、私の代わりにシリアライズのデータ​​ベースを使用するように傾斜させることだろう。あなたのニーズに応じて、選ぶべきものがたくさんあります。小さな組み込みデータベースの場合、H2のようなものが合理的な選択肢になります。 JavaFX and H2を統合する例をここに示します。

永続性については、JDBCまたはJPAをそのまま使用することができます。実質的なアプリケーションの場合、JPAが優れています。小さなアプリケーションでは、JDBCで十分です。 JPAを使用する場合は、Put together JavaFX properties and JPA Entities (NO MIXED MODE)にリンクされている記事と、JavaFX plus JPA exampleにリンクされているように、JavaFXプロパティベースのクラスと統合することができます。ただし、永続性のためにJavaFXビューモデルのプロパティオブジェクトを別々に保ち、DAO patternを使用したい場合があります。オブジェクトを別々に保つことで、アプリケーションの設計と実装の柔軟性はさらに向上しますが、DRYの原則に違反します。結果として得られるオブジェクトは、single responsibility principleをよりよく尊重するので、トレードオフです。

各エンティティ(ユーザー、アカウント、受信者、トランザクション)ごとに個別のテーブルを定義します。各エンティティエントリに一意のIDキーを割り当てます。 ObservableListsに保存したアイテム参照をリンクするにはrelationsを使用してください。

リモートロケーションからデータベースにアクセスしたいが、直接ポート接続を開くことができない場合は、データを提供するサーバー上のサービスを提供する必要があります(例えば、RESTベースのサーバーデータベースアクセスを実行し、JavaFXクライアントがRESTクライアントを介してアクセスするJSON over HTTPとして必要なデータを公開し、REST呼び出し応答をクライアントJavaFXプロパティベースのデータ構造に処理します)。このような実装はすばやく多くの仕事になります:-)

おそらく私はこれに答えてはいけません。質問や解釈はStackOverflowの原則では広すぎますが、おそらくここの情報はあなたにとって有益です。追加情報

に基づいて

具体的な答えは、私は実際には、既にDAOと春ブートベースのWebアプリケーションを持っており、それが正常に動作しているが、このJavaFXのアプリケーションがそのWebアプリケーションに接続するために計画されている休止状態。私はちょうどプログラムの少し "デモ"として、このローカルに保存されたファイルが必要です現在、インターネット接続が利用できない場合は、

ゴットチャ、それは完全に意味します。私は前にSpringBootとJavaFXを統合しましたが、残念ながらそれらの実装のソースを公開することはできません。

デモプログラムでは、JAXBまたはジャクソンによる永続性で十分です。 Makeryは、JavaFXのJAXBベースの永続性の良い例を提供します。

JAXBベースの手法のトリックは、ネストされたデータモデルで動作するものを得ることです。

JAXBベースのストレージ・アプローチの例

この例はMakery JavaFX tutorialのアイデアに基づいています。それをよりよく理解するには、チュートリアルを参照してください。入れ子にされた観測可能リストの持続性は、JAXB: How to marshal objects in lists?の概念を使用して達成されます。

解決策の鍵は、Userクラスのこのコードビットです。アカウントリストをネストされたObservableListとして提供し、標準アクセッサーaccounts()を提供して、JavaFXの規則に従ってObservableListを取得します。 ObservableListを標準Javaリストにコピーして、標準Javaリストにコピーし、JAXB @Xml...注釈を使用してゲッターに通知して、JAXBがユーザーにリンクされたアカウントのシリアライズとデシリアライズを処理できるようにする方法も提供しています(getAccounts()setAccounts())。

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がうまく動作しているspring-bootベースのWebアプリケーションを既に持っており、このJavaFX AppはそのWebアプリケーションに接続する予定です。私は現在、ローカルに保存されたファイルを、プログラムの少しデモとして必要としています。私はあなたが提供したH2の例を確かにチェックします! – UniqUnicorn

+0

あなたが提供した新しいJAXBサンプルで質問を更新しました! – UniqUnicorn

+0

@ UnniUnicornあなたのバージョンのコードでアンマーシャリングがうまくいかない理由がわかりません。私は私の答えでコードをダブルチェックし、アンマーシャリングは私が提供したコードのために働いています。私はあなたの実装のために経験する動作の違いの理由を知らない。 – jewelsea

関連する問題