2015-10-06 117 views
1

我正在使用JavaFX的模型实体属性witch允许我更改单个位置上的值,将它们绑定到UI并在模型实体数组中添加具有额外条件的更改侦听器(唯一值等)。JavaFX:基于属性的实体与基于属性的包装

我必须存储在数据库中的模型,所以这个问题是以下几点:

我应该改变我的模型实体到JPA实体(与AccessType.PROPERTY设定值)当我这样做没有必要需要所有值(或所有时间)作为属性或创建基于属性的包装器(面向)来访问它的基于经典值的实体类。

注意:某些可绑定属性根本不需要保留。

+0

你能澄清你的问题吗?您在问是否使用JavaFX属性实现JPA实体的好设计,实体有时将用于JavaFX上下文(需要可观察属性),有时用于不同的上下文(不需要任何功能从属性)? –

+0

其实这正是我所要求的。 – Wolfer

回答

2

不管你是否应该使用特定的技术都是基于观点的,所以我不会在这里回答你确切的问题。我只会提供一些有利有弊的选项。

在您的JPA注释的实体类中直接使用JavaFX属性的优点是您可以保持设计的简单性,只需一个类代表每个实体,而不用围绕实体注释类的包装类。

直接在JPA实体类使用JavaFX性能的缺点是:

  1. 潜在性能成本。 JavaFX属性比它们所表示的普通数据类型“重一些”,并且创建它们时有一定的代价,并且可能在为它们的听众创建容器时等。这可以通过一些冗长的代价来最小化(或者可能被消除),参见下面。
  2. 添加对JavaFX API的依赖关系。虽然JavaFX提供了来自Oracle的标准JDK,但它并不是JSE规范的必需部分,某些实现(例如OpenJDK)不包含它。这在实践中可能不是什么大问题,但你应该意识到这一点。

您可以通过使用“超级懒惰”模式来最小化使用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引擎,[德]将JSON序列化为PersonFX实例,并且在服务器上有一个JSON引擎,它[de]序列化相同的来自PersonEntity实例的JSON数据。由于JSON引擎会通过调用get/set方法来工作,所以对象本质上具有相同的形式。自从我开始使用JavaFX之后,我还没有对XML数据进行序列化,所以我不确定同样的方法是否适用于XML数据,但我认为您也可以完成这项工作。您甚至可以在Java序列化数据流中执行此操作,方法是在实现类中定义readObjectwriteObject方法,这些方法需要相同形式的数据(或使用序列化代理)。

+0

更多我想到它我总是回到某种包装(谢谢你的方式:http://stackoverflow.com/questions/23522130 :-)。应用程序不需要处理来自多个来源的数据(序列化)。实体将主要代表统计结果(在大多数情况下)。不过,我必须为这些数据创建一个手动工具来测量和插入。这可能会在功能上发生变化,可能会创建新的(更自动的)工具,或者可能会引入不同的测量方式。包装可以帮助我独立调整每个案例的外观。 – Wolfer