在模型类中使用javafx.beans属性

在模型类中使用JavaFX bean属性是否正确?

我想知道在模型类中使用属​​性是否能很好地将它们与视图组件绑定起来是一个好习惯。 我并不担心将来这些库的可用性,因为我的程序将在JRE8或更高版本上运行,但是在模型类中使用JavaFX库的性质使我感到怀疑,我特别担心当前和未来的不兼容问题,我想用Hibernate来坚持这些属性。

注意:我使用纯JavaFX环境,并且在我的应用程序中我永远不需要Swing兼容性。

我要在这里提出一些不同意见。

JavaFX属性和JPA

正如我评论jewelsea的答案,只要您使用“属性访问”而不是“字段访问”,使用基于JavaFX属性的bean与JPA是可能的。 我链接的博客文章更详细地介绍了这一点,但基本的想法是,任何注释应该在get...()方法上,而不是在字段上。 据我所知,这确实阻止了与JPA结合使用任何只读的JavaFX属性模式,但我从来没有真正感觉到JPA在只读属性(即get方法和无set方法)方面performance良好。

序列化

与我对jewelsea的答案的评论相反,并且用了几个星期的时间来处理这个问题(并且被放置在一个我正在使用JavaFX属性在JavaFX客户端复制几个实体类的位置),我认为缺乏JavaFX属性的序列化可以被解决。 关键的观察是,你真的只需要序列化属性的包装状态(不,例如,任何听众)。 你可以通过实现java.io.Externalizable来做到这一点。 ExternalizableSerializable的子接口,需要您填写readExternal(...)writeExternal(...)方法。 这些方法可以被实现为只将属性包装的状态,而不是属性本身。 这意味着,如果您的实体被序列化,然后反序列化,您将最终得到一个新的属性实例,任何听众将不会被保留(即听众有效成为transient ),但据我所知,这将是什么想在任何合理的用例。

我尝试用这种方式定义的bean,这似乎很好地工作。 另外,我运行了一个小实验,在客户端和一个安静的web服务之间进行转换,使用Jackson映射器转换为JSON表示。 由于mapper只依赖于使用get和set方法,所以这工作得很好。

一些注意事项

有几点需要注意。 和任何序列化一样,有一个无参数的构造函数是很重要的。 当然,由JavaFX属性包装的所有值本身都必须是可序列化的 – 这同样适用于任何可序列化的bean。

关于JavaFX属性通过副作用工作的观点已经很好了,在将这些属性(在某种程度上,devise时考虑到单线程模型)转移到潜在的multithreading服务器时,需要注意。 一个好的经验法则是,如果你使用这个策略,监听器只能在客户端注册(并且记住,这些监听器在传输回服务器方面是暂时的,无论是通过序列化还是通过JSON表示)。 当然,这表明在服务器端使用这些可能是一个糟糕的devise; 它成为了一个单一实体的便利之间的平衡点,这个实体对于所有人来说都是“所有东西”(JavaFX客户端的可观察属性,可序列化用于持久化和/或远程访问,以及用于JPA的持久映射)与公开function(如可观察性),可能不完全合适(在服务器上)。

最后,如果您使用JPA注释,那么这些注释会具有运行时保留,这意味着(我认为)您的JavaFX客户端将需要类path上的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物业devise

JavaFX属性的devise使您无需运行JavaFX程序即可使用它们。 使用JavaFX属性和绑定教程的Oracle部分演示了这种用法(例如,Bill类来模拟账单的属性)。 本教程中的示例只是运行一个标准的Java程序,而不使用JavaFX应用程序 。 所以你可以一般地使用属性和绑定,而不需要额外的JavaFX运行时。 这意味着你可以例如在服务器端应用程序中使用JavaFX属性和绑定。

“正确的”做法

好的,所以你可以这样做,但这是“正确的”做法吗?

我不认为很多人以这种方式使用JavaFX属性。 原因之一就是因为JavaFX的属性是相当新的。 我不认为在模型对象中使用JavaFX属性是“错误的”。

注意事项

JavaFX属性不支持Java序列化(我的意思是直接支持Serializable接口)。 众多的服务器端Java技术可能需要模型序列化,并且他们将无法序列化任何使用JavaFX属性的对象。

JavaFX属性本身并不是容器感知的,并且通过副作用来工作(例如,改变一个属性可能会触发对另一个绑定值的更新),所以请注意这一点,并确保这种处理在您的环境中是可接受的方法。 尤其要注意的是,在multithreading服务器环境中,不要产生不需要的竞争条件(JavaFX客户端应用程序通常不需要太多的关注,因为一般来说,JavaFX是作为单线程环境运行的)。

JavaFX属性和Hibernate / JPA

我不认为将JavaFX属性混合到Hibernate(或JPA)实体类中是一个好主意。 我还没有看到有人这样做。 Hibernate本身并不知道JavaFX的属性,一般来说它是针对像String和Ints这样的Java原语而devise的,所以我不知道它是如何自动将JavaFX属性映射到数据库字段的。

您可能需要一个为您的JavaFX属性模型类定义您的实体类层次结构和并行层次结构的设置,最后还需要在两者之间映射的映射器层。 这种架构设置本质上是一个MVVM模型 。 模型(M)是您的Hibernate实体类,而视图模型(VM)是您的基于JavaFX属性的模型。

持久jpa集合只是使用这个签名的getter方法..

 @OneToMany List<Person> getAll() { return observableList; }