2015-10-06 159 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