モデルクラスでJavaFX Beanプロパティを使用することは正しい習慣ですか?
ビューコンポーネントでそれらをより簡単にバインドできるようにするために、モデルクラスでプロパティを使用することは良い習慣であるのでしょうか。私のプログラムはJRE8以降で実行されるため、これらのライブラリーの将来の可用性については心配していませんが、モデルクラスでJavaFXライブラリーを使用する性質には懐疑的であり、特に現在および将来の非互換性について心配していますHibernateを使用してこれらの属性を永続化したいと考えています。
注:純粋なJavaFX環境を使用しており、アプリケーションにSwing互換性が必要になることはありません。
ここで私は反対意見を述べたいと思います。
JavaFXプロパティとJPA
Jewelseaの回答についてコメントしたように、「フィールドアクセス」ではなく「プロパティアクセス」を使用する限り、JPAでJavaFXプロパティベースのBeanを使用することが可能です。 ブログ投稿 ここでリンクしましたが、これについて詳しく説明しますが、基本的な考え方は、注釈はフィールドではなくget...()
メソッドにある必要があるということです。私の知る限り、これはJPAと組み合わせて読み取り専用のJavaFXプロパティパターンを使用できないようにしますが、いずれにしても、JPAが読み取り専用プロパティ(つまり、getメソッドとsetメソッドなし)でうまく機能しているとはまったく感じていません。 。
シリアライゼーション
Jewelseaの回答に関する私のコメントとは異なり、これに取り組むのに数週間のメリットがある(そして、JavaFXプロパティを使用してJavaFXクライアント側でいくつかのエンティティークラスを複製することに直面していた位置に置かれていた)ので、 JavaFXプロパティのシリアル化の問題を回避できます。重要な所見は、実際にはプロパティのラップされた状態(たとえば、リスナーではない)のみをシリアル化する必要があるということです。これを行うには、_Java.io.Externalizable
_を実装します。 Externalizable
はSerializable
のサブインターフェイスであり、readExternal(...)
およびwriteExternal(...)
メソッドに入力する必要があります。これらのメソッドを実装して、プロパティ自体ではなく、プロパティによってラップされた状態のみを外部化できます。つまり、エンティティがシリアル化されてから逆シリアル化されると、新しいプロパティインスタンスが作成され、リスナーは保持されません(つまり、リスナーは事実上transient
になります)。これは、合理的なユースケースで必要なものです。
この方法で定義されたBeanを実験しましたが、すべてうまく機能しているようです。さらに、Jacksonマッパーを使用してJSON表現に変換したり、JSON表現から変換したりして、クライアントと残りのWebサービス間でそれらを転送する小さな実験を行いました。マッパーはgetメソッドとsetメソッドの使用に依存しているだけなので、これは問題なく機能します。
いくつかの警告
いくつかの点に注意する必要があります。他のシリアライゼーションと同様に、引数のないコンストラクターを持つことが重要です。そしてもちろん、JavaFXプロパティによってラップされるすべての値は、それ自体がシリアライズ可能でなければなりません。これも、シリアライズ可能なBeanの場合と同じルールです。
副作用を介して機能するJavaFXプロパティについての要点はよく理解されており、これらのプロパティ(ある程度はシングルスレッドモデルを念頭に置いて設計されています)をマルチスレッドの可能性があるサーバーに移動するときは注意が必要です。経験則として、この戦略を使用する場合、リスナーはクライアント側でのみ登録する必要があります(ただし、これらのリスナーは、シリアル化またはJSON表現によるサーバーへの転送に関して一時的です)。もちろん、サーバー側でこれらを使用するのは悪い設計になる可能性があることを示唆しています。 「すべての人にとってすべてのもの」である単一エンティティ(JavaFXクライアントの監視可能なプロパティ、永続性またはリモートアクセス、あるいはその両方のシリアル化可能、およびJPAの永続的なマッピング)の利便性と公開機能の間のトレードオフになります。 (たとえば、観測可能性)(サーバー上で)完全に適切ではない可能性がある場合。
最後に、JPAアノテーションを使用する場合、それらにはランタイム保持があり、JavaFXクライアントがクラスパスにjavax.persistence仕様を必要とすることを意味します(私はそう思います)。
このような「オールシーズンの男性」エンティティの例を次に示します。
_import Java.io.Externalizable;
import Java.io.IOException;
import Java.io.ObjectInput;
import Java.io.ObjectOutput;
import Java.time.MonthDay;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* Entity implementation class for Entity: Person
*
*/
@Entity
public class Person implements Externalizable {
private static final long serialVersionUID = 1L;
public Person() {
}
public Person(String name, MonthDay birthday) {
setName(name);
setBirthday(birthday);
}
private final IntegerProperty id = new SimpleIntegerProperty(this, "id");
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
public IntegerProperty idProperty() {
return id ;
}
private final StringProperty name = new SimpleStringProperty(this, "name");
// redundant, but here to indicate that annotations must be on the property accessors:
@Column(name="name")
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name ;
}
private final ObjectProperty<MonthDay> birthday = new SimpleObjectProperty<>();
public final MonthDay getBirthday() {
return birthday.get();
}
public final void setBirthday(MonthDay birthday) {
this.birthday.set(birthday);
}
public ObjectProperty<MonthDay> birthdayProperty() {
return birthday ;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(getId());
out.writeObject(getName());
out.writeObject(getBirthday());
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
setId(in.readInt());
setName((String) in.readObject());
setBirthday((MonthDay) in.readObject());
}
}
_
JavaFXプロパティデザイン
JavaFXプロパティは、それらを使用するためにJavaFXプログラムを実行する必要がないように設計されています。 OracleはJavaFXプロパティとバインディングチュートリアルを使用 のセクションで、このような使用法を示します(たとえば、請求書のプロパティをモデル化するBillクラス)。チュートリアルのサンプルは、標準のJava main
を使用して JavaFX Application ではなくプログラムを実行するだけです。したがって、プロパティとバインディングを使用せずに一般的に使用できます。つまり、JavaFXランタイムに追加の要件があります。これは、たとえば、サーバー側アプリケーションでJavaFXプロパティとバインディングを利用できることを意味します。
「正しい」プラクティス
OK、それであなたはそれをすることができますが、それは「正しい」練習ですか?
多くの人がこのようにJavaFXプロパティを使用しているとは思いません。その理由の1つは、JavaFXプロパティが非常に新しいためです。モデルオブジェクトでJavaFXプロパティを使用することは「間違っている」とは思わない。
注意事項
JavaFXプロパティはJavaシリアライゼーションをサポートしていません(つまり、 Serializable インターフェースの直接サポートを意味します)。多数のサーバー側Javaテクノロジーモデルのシリアル化が必要な場合があり、JavaFXプロパティを使用するオブジェクトをシリアル化できません。
JavaFXプロパティ自体はコンテナに対応しておらず、副作用を介して機能します(たとえば、プロパティを変更すると、別のバインドされた値への更新がトリガーされる場合があります)。このことを認識し、この種の処理が環境で許容できるアプローチであることを確認してください。特に、マルチスレッドサーバー環境で不要な競合状態を生成しないように注意してください(JavaFXクライアントアプリケーションは、一般に、JavaFXは一般にシングルスレッド環境として実行されるため、通常、これに関する注意はそれほど必要ありません)。
JavaFXプロパティとHibernate/JPA
JavaFXプロパティをHibernate(またはJPA)エンティティークラスに混在させることは良い考えではないと思います。誰もそうするのを見たことがありません。 Hibernate自体はJavaFXプロパティを認識せず、一般にJava Stringsやintなどのプリミティブに対して機能するように設計されているため、JavaFXプロパティをデータベースに自動的にマップする方法がわかりませんフィールド。
おそらく、エンティティークラス階層とJavaFXプロパティベースのモデルクラスの並列階層を定義する設定と、最後に2つの間にマッピングするマッパーレイヤーが必要になります。この種のアーキテクチャ設定は、本質的には MVVMモデル です。モデル(M)はHibernateエンティティークラスであり、ビューモデル(VM)はJavaFXプロパティベースのモデルです。
jpaコレクションを永続化するには、この署名をゲッターメソッドに使用します。
@OneToMany
List<Person> getAll()
{
return observableList;
}