Usar las propiedades de javafx.beans en las clases modelo

¿Es una práctica correcta usar propiedades de beans JavaFX en las clases modelo?

Me pregunto si es una buena práctica usar propiedades en las clases modelo para poder enlazarlas más fácilmente con los componentes de la vista. No estoy preocupado por la disponibilidad de esas bibliotecas en el futuro porque mi progtwig se ejecutará en JRE8 o posterior, pero la naturaleza del uso de las bibliotecas JavaFX en las clases modelo me hace escéptico y me preocupan las incompabilities actuales y futuras especialmente porque Quiero usar Hibernate para persistir esos atributos.

Nota: Utilizo un entorno JavaFX puro y nunca necesitaré la compatibilidad Swing en mi aplicación.

Voy a ofrecer algo de una opinión disidente aquí.

Propiedades JavaFX y JPA

Como comenté en la respuesta de jewelsea, usar un bean basado en propiedades JavaFX con JPA es posible siempre que use “acceso a propiedades” en lugar de “acceso a campos”. La publicación del blog que vinculé allí entra en más detalles sobre esto, pero la idea básica es que cualquier anotación debe estar en los métodos get...() y no en los campos. Por lo que puedo ver, esto evita el uso de cualquiera de los patrones de propiedad JavaFX de solo lectura junto con JPA, pero nunca sentí realmente que JPA funcionara bien con propiedades de solo lectura (es decir, obtener métodos y ningún método de configuración) de todos modos .

Publicación por entregas

Contrariamente a mi comentario sobre la respuesta de jewelsea, y con la ventaja de unas pocas semanas para trabajar con esto (y habiendo sido colocado en una posición donde estaba enfrentando la replicación de varias clases de entidades en un lado del cliente JavaFX usando propiedades JavaFX), creo que la falta de la serialización de las propiedades de JavaFX se puede solucionar. La observación clave es que realmente solo necesita serializar el estado envuelto de la propiedad (no, por ejemplo, ningún oyente). Puedes hacer esto implementando java.io.Externalizable . Externalizable es una interfaz secundaria de Serializable que requiere que complete los readExternal(...) y writeExternal(...) . Estos métodos pueden implementarse para externalizar solo el estado envuelto por la propiedad, en lugar de la propiedad en sí misma. Esto significa que si su entidad se serializa y luego se deserializa, terminará con una nueva instancia de propiedad, y no se conservarán los oyentes (es decir, los oyentes se volverán transient ), pero hasta donde puedo ver, esto sería lo que era querido en cualquier caso de uso razonable.

Experimenté con frijoles definidos de esta manera y todo parece funcionar muy bien. Además, realicé un pequeño experimento al transferirlos entre el cliente y un servicio web tranquilo, usando el mapeador Jackson para convertir ay desde una representación JSON. Dado que el asignador simplemente depende del uso de los métodos get y set, esto funciona bien.

Algunas advertencias

Se deben observar un par de puntos. Al igual que con cualquier serialización, es importante tener un constructor sin argumentos. Y, por supuesto, todos los valores envueltos por las propiedades de JavaFX deben ser serializables. De nuevo, esta es la misma regla que para cualquier bean serializable.

El punto acerca de las propiedades de JavaFX que funcionan a través de los efectos secundarios está bien tomado, y se debe tener cuidado al mover estas propiedades (que están, en cierta medida, diseñadas con un modelo de subproceso único en mente) a un servidor potencialmente multiproceso. Una buena regla empírica es probablemente que si usa esta estrategia, los oyentes solo deben registrarse en el lado del cliente (y recuerde, esos oyentes son transitorios con respecto a la transferencia de regreso al servidor, ya sea por serialización o por representación JSON). Por supuesto, eso sugiere que usar estos en el lado del servidor puede ser un mal diseño; se convierte en una compensación entre la comodidad de tener una única entidad que sea “todo para todas las personas” (propiedades observables para el cliente JavaFX, serializable para persistencia y / o acceso remoto, y con mapeos persistentes para JPA) versus exposición de funcionalidad (por ejemplo, observabilidad) donde podría no ser totalmente apropiado (en el servidor).

Finalmente, si usa las anotaciones JPA, esas tienen retención de tiempo de ejecución, lo que implica (creo) que su cliente JavaFX necesitará la especificación javax.persistence en la ruta de clase.

Aquí hay un ejemplo de tal entidad de “hombre para todas las estaciones”:

 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 birthday = new SimpleObjectProperty<>(); public final MonthDay getBirthday() { return birthday.get(); } public final void setBirthday(MonthDay birthday) { this.birthday.set(birthday); } public ObjectProperty 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()); } } 

Diseño de propiedad JavaFX

Las propiedades de JavaFX están diseñadas de tal manera que no necesita ejecutar un progtwig JavaFX para usarlas. Las secciones de Oracle Using JavaFX Properties and Binding Tutorial , demuestran dicho uso (por ejemplo, una clase de Bill para modelar las propiedades de una factura). El ejemplo del tutorial solo ejecuta un progtwig Java estándar con una aplicación main y no una aplicación JavaFX . Por lo tanto, puede usar genéricamente propiedades y enlaces sin tener un requisito adicional en el tiempo de ejecución de JavaFX. Esto significa que podría, por ejemplo, hacer uso de propiedades y enlaces JavaFX en una aplicación del lado del servidor.

Prácticas “correctas”

OK, entonces puedes hacerlo, pero ¿es una práctica “correcta”?

No creo que muchas personas usen las propiedades de JavaFX de esta manera. Una razón para esto es simplemente porque las propiedades de JavaFX son bastante nuevas. No creo que sea “incorrecto” usar propiedades JavaFX en objetos modelo.

Advertencias

Las propiedades de JavaFX no admiten la serialización de Java (con esto quiero decir soporte directo para la interfaz Serializable ). Numerosas tecnologías de Java del lado del servidor podrían requerir serialización del modelo y no podrían serializar ningún objeto que hiciera uso de las propiedades de JavaFX.

Las propias propiedades de JavaFX no son conscientes de los contenedores y funcionan a través de efectos secundarios (por ejemplo, cambiar una propiedad puede desencadenar una actualización a otro valor enlazado), así que tenga en cuenta esto y asegúrese de que este tipo de procesamiento sea aceptable en su entorno. En particular, tenga cuidado de no generar condiciones de carrera no deseadas en un entorno de servidor de subprocesos múltiples (las aplicaciones de cliente JavaFX generalmente requieren menos cuidado con respecto a esto ya que JavaFX en general se ejecuta principalmente como un entorno de subproceso único).

Propiedades de JavaFX e Hibernate / JPA

No creo que sea una buena idea mezclar propiedades JavaFX en clases de entidades Hibernate (o JPA). No he visto a nadie hacer eso. Hibernate en sí no tiene conocimiento de las propiedades de JavaFX y, en general, está diseñado para trabajar con primitivas Java como Strings y ints, por lo que no sé cómo podría mapear automáticamente una propiedad JavaFX a un campo de base de datos.

Es probable que necesite una configuración que defina su jerarquía de clase de entidad y una jerarquía paralela para sus clases de modelo basadas en propiedades JavaFX y, finalmente, una capa de correlacionador que mapee entre las dos. Este tipo de configuración arquitectónica es esencialmente un modelo MVVM . El modelo (M) es su clase de entidad Hibernate, y el modelo de vista (VM) es su modelo basado en la propiedad JavaFX.

para las colecciones persistentes de jpa simplemente use esta firma para obtener el método getter.

 @OneToMany List getAll() { return observableList; }