在模型类中使用JavaFX bean属性是否正确?
我想知道在模型类中使用属性是否能够更容易地将它们与视图组件绑定是一个好习惯。我以后不担心这些库的可用性,因为我的程序将在JRE8或更高版本上运行,但在模型类中使用JavaFX库的性质让我持怀疑态度并且我担心当前和未来的不可能性特别是因为我想使用Hibernate来保持这些属性。
注意:我使用纯JavaFX环境,在我的应用程序中永远不需要Swing兼容性。
答案 0 :(得分:18)
我将在这里提出一些不同意见。
JavaFX属性和JPA
正如我对jewelsea的回答一样,只要您使用“属性访问”而不是“字段访问”,就可以使用基于JavaFX属性的bean和JPA。我在其中链接的blog post详细介绍了这一点,但基本思路是任何注释都应该在get...()
方法上,而不是在字段上。据我所知,这确实阻止了任何只读JavaFX属性模式与JPA的结合使用,但我从来没有真正觉得JPA在只读属性(即获取方法和没有设置方法)方面表现良好
<强>序列化强>
与我对jewelsea的答案的评论相反,并且利用几周的时间来处理这个问题(并且我已经被置于一个使用JavaFX属性在JavaFX客户端上复制几个实体类的位置),认为缺乏JavaFX属性的序列化可以解决。关键的观察是你真的只需要序列化属性的包装状态(例如,不是任何监听器)。您可以通过实施java.io.Externalizable
来实现此目的。 Externalizable
是Serializable
的子界面,需要您填写readExternal(...)
和writeExternal(...)
方法。可以实现这些方法来仅外化属性包装的状态,而不是属性本身。这意味着如果您的实体被序列化然后反序列化,您将最终得到一个新的属性实例,并且不会保留任何侦听器(即侦听器实际上变为transient
),但据我所知在任何合理的用例中都会被通缉。
我尝试用这种方式定义的bean,这一切似乎都很好用。另外,我在客户端和一个安静的Web服务之间进行了一个小实验,使用Jackson映射器转换为JSON表示和从JSON表示转换。由于mapper只依赖于使用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());
}
}
答案 1 :(得分:9)
JavaFX Property Design
JavaFX属性的设计使您无需运行JavaFX程序即可使用它们。 Oracle Using JavaFX Properties and Binding Tutorial的各个部分演示了这种用法(例如,用于对账单属性进行建模的Bill类)。本教程中的示例只运行标准Java程序,main
而不是JavaFX Application。因此,您可以通常使用属性和绑定,而无需对JavaFX运行时进行额外要求。这意味着您可以在服务器端应用程序中使用JavaFX属性和绑定。
“纠正”做法
好的,所以你可以做到,但这是“正确的”练习吗?
我不认为很多人会以这种方式使用JavaFX属性。这样做的一个原因仅仅是因为JavaFX属性非常新。我不认为在模型对象中使用JavaFX属性是“错误的”。
<强>注意事项强>
JavaFX属性不支持Java序列化(我的意思是直接支持Serializable接口)。许多服务器端Java技术可能需要模型序列化,并且它们无法序列化任何使用JavaFX属性的对象。
JavaFX属性本身不是容器感知的,并且通过副作用工作(例如,更改属性可能会触发对另一个绑定值的更新),因此请注意这一点,并确保这种处理是您可接受的方法。环境。特别要注意的是,在多线程服务器环境中不要产生不必要的竞争条件(JavaFX客户端应用程序通常需要更少关注,因为JavaFX通常主要作为单线程环境运行)。
JavaFX属性和Hibernate / JPA
我认为将JavaFX属性混合到Hibernate(或JPA)实体类中并不是一个好主意。我没见过有人那样做过。 Hibernate本身并不了解JavaFX属性,并且通常设计用于处理像Strings和ints这样的Java原语,因此我不知道如何将JavaFX属性自动映射到数据库字段。
您可能需要一个设置,它定义您的实体类层次结构和基于JavaFX属性的模型类的并行层次结构,最后是映射两者之间的映射器层。这种架构设置基本上是MVVM model。模型(M)是您的Hibernate实体类,视图模型(VM)是基于JavaFX属性的模型。
答案 2 :(得分:0)
对于持久化jpa集合,只需将此签名用于getter方法..
@OneToMany
List<Person> getAll()
{
return observableList;
}