我正在使用由JavaFX属性组成的模型实体,允许我更改单个位置的值,将它们绑定到UI并在模型实体数组中添加具有额外条件的更改的侦听器(唯一值等)。
我必须将模型存储在数据库中,所以问题如下:
当我不需要将所有值(或所有时间)作为属性或使用属性创建基于经典值的实体类时,我是否应该将模型实体转换为JPA实体(使用AccessType.PROPERTY来设置值)基于包装器(面对)来访问它。
注意:某些可绑定属性根本不需要保留。
答案 0 :(得分:2)
您是否应该使用特定技术是非常基于意见的,所以我不会在这里回答您的确切问题。我将提供一些有利有弊的选择。
直接在JPA注释的实体类中使用JavaFX属性的优点是保持设计简单,只有一个类代表每个实体,而不是围绕实体注释类的包装类。
直接在JPA实体类中使用JavaFX属性的缺点是:
您可以使用“超级懒惰”模式最小化使用JavFX属性的成本。这里只有在实际使用它们时才会创建属性对象(例如,如果你用它们注册一个监听器);否则使用具有相同数据类型的代理字段:
@Entity
@Access(AccessType.PROPERTY)
public class Person {
private IntegerProperty age ;
private int _age ;
private StringProperty name ;
private String _name ;
private int id ;
@Id
public int getId() {
return id ;
}
public void setId(int id) {
this.id = id ;
}
public IntegerProperty ageProperty() {
if (age == null) {
age = new SimpleIntegerProperty(_age);
}
return age ;
}
public int getAge() {
if (age == null) {
return _age ;
} else {
return age.get();
}
}
public void setAge(int age) {
if (this.age == null) {
_age = age ;
} else {
this.age.set(age);
}
}
public StringProperty nameProperty() {
if (name == null) {
name = new SimpleStringProperty(_name);
}
return name ;
}
public String getName() {
if (name == null) {
return _name ;
} else {
return name.get();
}
}
public void setName(String name) {
if (this.name == null) {
_name = name ;
} else {
this.name.set(name);
}
}
}
这基本上避免了(几乎)由于在非JavaFX环境中使用此类而导致的任何性能开销,因为除非通过xxxProperty()
方法明确请求,否则不会实例化属性。请注意,调用这些方法是注册侦听器的唯一方法,因此如果注册了侦听器,则代码保证在随后调用setXxx(...)
时通知这些侦听器。这里的成本是一些代码冗长和一些冗余空值检查的非常小的成本(JVM可能无论如何都会优化)。
这种技术显然无法解决对JavaFX API的依赖问题。
另一种可能的选择是使用带有属性更改侦听器的普通JavaBean:
@Entity
public class Person {
@Id
private int id ;
private int age ;
private String name ;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public int getAge() {
return age ;
}
public void setAge(int age) {
int oldAge = age ;
this.age = age ;
pcs.firePropertyChange("age", oldAge, age);
}
public String getName() {
return name ;
}
public void setName(String name) {
String oldName = name ;
this.name = name ;
pcs.firePropertyChange("name", oldName, name);
}
// ...
}
现在,在JavaFX客户端中,您可以执行以下操作:
TableView<Person> contactTable = new TableView<>();
TableColumn<Person, String> nameCol = new TableView<>("Name");
nameCol.setCellValueFactory(cellData -> {
try {
return JavaBeanStringPropertyBuilder.create()
.bean(cellData.getValue())
.name("name")
.build();
} catch (Exception exc) {
return new RuntimeException(exc);
}
});
这里的好处是您的实体完全没有依赖JavaFX。成本是您的客户端代码更加冗长,并且缺少编译时检查是否存在正确的方法。这里有相当多的反思,因此在评估客户端代码中的属性时,您将获得一些(可能是次要的)性能损失。有关详细信息,请参阅JavaBean wrapping with JavaFX Properties。
我猜其他一条评论可能是恰当的。您可能希望在JavaFX上下文中使用实体的最常见用例是既有Web应用程序又有独立(JavaFX)客户端应用程序,它们都通过Web服务访问数据(例如提供和使用JSON)。在这种情况下,您可以考虑并行保留两组类,一组使用JavaFX属性,另一组实现为Java bean。由于get/set
方法的集合是相同的,因此JSON序列化程序应该能够将相同的JSON表示转换为任一形式。您可以使用界面保持两个表单同步:
public interface Person {
public int getAge() ;
public void setAge(int age) ;
public String getName() ;
public void setName(String name) ;
}
有明显的实施
@Entity
public class PersonEntity implements Person {
private int age ;
private String name ;
@Id
private int id ;
// get/set methods omitted...
}
和
public class PersonFX implements Person {
private final StringProperty name = new SimpleStringProperty() ;
private final IntegerProperty age = new SimpleIntegerProperty() ;
public StringProperty nameProperty() {
return name ;
}
@Override
public final String getName() {
return nameProperty().get();
}
@Override
public final void setName(String name) {
nameProperty().set(name);
}
// similarly for age...
}
现在在JavaFX客户端中,您可以拥有一个JSON引擎[de]将JSON序列化为PersonFX
个实例,并且在服务器上有一个JSON引擎[de]序列化相同< / em>来自PersonEntity
实例的JSON数据。由于JSON引擎只能通过调用get / set方法来工作,因此对象本质上从它的角度来看具有相同的形式。自从我开始使用JavaFX以来,我没有处理过/从XML数据序列化,因此我不确定相同的方法是否适用于XML数据,但我认为你也可以做到这一点。您甚至可以使用Java序列化流来执行此操作,方法是在实现类中定义readObject
和writeObject
方法,这些方法需要相同形式的数据(或使用序列化代理)。