在模型类中使用 javafx.beans 属性
Posted
技术标签:
【中文标题】在模型类中使用 javafx.beans 属性【英文标题】:Using javafx.beans properties in model classes 【发布时间】:2014-06-04 23:05:52 【问题描述】:在模型类中使用 JavaFX bean 属性是否正确?
我想知道在模型类中使用属性以便能够更轻松地将它们与视图组件绑定是否是一种好习惯。我不担心将来这些库的可用性,因为我的程序将在 JRE8 或更高版本上运行,但是在模型类中使用 JavaFX 库的性质让我持怀疑态度,我特别担心当前和未来的不兼容性,因为我想使用 Hibernate 来持久化这些属性。
注意:我使用的是纯 JavaFX 环境,我的应用程序永远不需要 Swing 兼容性。
【问题讨论】:
【参考方案1】:JavaFX 属性设计
JavaFX 属性的设计使您无需运行 JavaFX 程序即可使用它们。 Oracle Using JavaFX Properties and Binding Tutorial 的部分演示了这种用法(例如,用于模拟账单属性的 Bill 类)。本教程中的示例仅运行带有main
而不是JavaFX Application 的标准Java 程序。因此,您可以通用地使用属性和绑定,而无需对 JavaFX 运行时有额外要求。例如,这意味着您可以在服务器端应用程序中使用 JavaFX 属性和绑定。
“正确”的做法
好的,所以你可以做到,但这是“正确”的做法吗?
我认为没有多少人以这种方式使用 JavaFX 属性。一个原因仅仅是因为 JavaFX 属性是相当新的。我不认为在模型对象中使用 JavaFX 属性是“错误的”。
注意事项
JavaFX 属性不支持 Java 序列化(我的意思是直接支持 Serializable 接口)。许多服务器端 Java 技术可能需要模型序列化,它们将无法序列化任何使用 JavaFX 属性的对象。
JavaFX 属性本身不是容器感知的,并且通过副作用工作(例如,更改属性可能会触发对另一个绑定值的更新),因此请注意这一点并确保这种处理是您的可接受的方法环境。特别是,请注意不要在多线程服务器环境中生成不需要的竞争条件(JavaFX 客户端应用程序通常需要较少注意这一点,因为 JavaFX 通常主要作为单线程环境运行)。
JavaFX 属性和 Hibernate/JPA
我不认为将 JavaFX 属性混合到 Hibernate(或 JPA)实体类中是一个好主意。我还没有看到有人这样做。 Hibernate 本身不知道 JavaFX 属性,并且通常设计用于处理 Java 原语,如字符串和整数,所以我不知道它如何可能自动将 JavaFX 属性映射到数据库字段。
您可能需要一个设置来定义实体类层次结构和基于 JavaFX 属性的模型类的并行层次结构,最后是在两者之间映射的映射器层。这种架构设置本质上是MVVM model。模型 (M) 是您的 Hibernate 实体类,视图模型 (VM) 是您基于 JavaFX 属性的模型。
【讨论】:
谢谢!所以毫无疑问!我不能在模型和实体类中使用 JavaFX 属性!即使在像我这样的客户端应用程序中。可能有几个大问题!我没有注意到 JavaFX 属性不可序列化。 为了它的价值,我have seen JavaFX properties used in conjunction with JPA。它工作得很好,因为 JPA 尊重 JFX 属性扩展的标准 JavaBean 模式 (get/setXXX)。不过,由于序列化问题,我不相信它在任何实际场景中都特别有用。 你能给我一个你建议在 JavaFX - JPA 应用程序中实现的 MVVM 模型的简单示例吗?任何教程或演练或示例代码?非常感谢。【参考方案2】:我将在这里提出一些不同的意见。
JavaFX 属性和 JPA
正如我对jewelsea 的回答所评论的那样,只要您使用“属性访问”而不是“字段访问”,就可以将基于JavaFX 属性的bean 与JPA 结合使用。我在那里链接的blog post 对此进行了更详细的说明,但基本思想是任何注释都应该在get...()
方法上,而不是在字段上。据我所知,这确实阻止了将任何只读 JavaFX 属性模式与 JPA 结合使用,但我从来没有真正觉得 JPA 与只读属性(即 get 方法和无 set 方法)配合得很好.
序列化
与我对 Jewelsea 的回答的评论相反,并且有几个星期的时间来处理这个问题(并且已经被放置在我面临使用 JavaFX 属性在 JavaFX 客户端复制多个实体类的位置),我认为可以解决 JavaFX 属性缺乏序列化的问题。关键的观察是您实际上只需要序列化属性的包装状态(例如,不需要任何侦听器)。您可以通过实现java.io.Externalizable
来做到这一点。 Externalizable
是Serializable
的子接口,需要填写readExternal(...)
和writeExternal(...)
方法。可以实现这些方法以仅外部化由属性包装的状态,而不是属性本身。这意味着如果您的实体被序列化然后反序列化,您最终将得到一个新的属性实例,并且不会保留任何侦听器(即侦听器实际上变为transient
),但据我所知,这将是在任何合理的用例中都需要什么。
我尝试了以这种方式定义的 bean,并且一切似乎都运行良好。此外,我进行了一个小型实验,在客户端和一个 RESTful Web 服务之间传输它们,使用 Jackson 映射器在 JSON 表示形式之间进行转换。由于映射器仅依赖于使用 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 集合怎么样? Hibernate 有映射到列表/集合的方法,但 AFAIK 它不会映射 JavaFX 集合。是否有实现 OneToMany/ManyToMany 关系并使用 FXCollections(ObservableList 或其他)支持它们的模式? 是的,好点。我暂时忘记了PersistentCollection
s 的混乱。我需要考虑一下(现在没有时间)。这绝对是可能的,但可能涉及实现UserCollectionType
,这可能会很快变得丑陋。
如果您确实花时间考虑这一点并提出一些建议,请告诉我。无论如何,我现在要尝试实现 UserCollectionType。 Hibernate 非常适合为我节省数百行样板代码,但它与 javaFX 属性/集合的相对不友好有时很粗糙。【参考方案3】:
对于持久化 jpa 集合,只需将此签名用于 getter 方法。
@OneToMany
List<Person> getAll()
return observableList;
【讨论】:
嗨,谢谢。相关映射如何。我有问题here也许你能帮忙?以上是关于在模型类中使用 javafx.beans 属性的主要内容,如果未能解决你的问题,请参考以下文章