web-dev-qa-db-fra.com

Utilisation des propriétés javafx.beans dans les classes de modèle

Est-ce une pratique correcte d'utiliser des propriétés de beans JavaFX dans les classes de modèle?

Je me demande s'il est judicieux d'utiliser des propriétés dans les classes de modèle pour pouvoir les lier plus facilement avec les composants de vue. Je ne m'inquiète plus de la disponibilité de ces bibliothèques à l'avenir car mon programme sera exécuté sur JRE8 ou une version ultérieure, mais la nature de l'utilisation des bibliothèques JavaFX dans les classes de modèle me rend sceptique et m'inquiète surtout des incompatibilités actuelles et futures, car Je souhaite utiliser Hibernate pour conserver ces attributs.

Remarque: j'utilise un environnement JavaFX pur et je n'aurai jamais besoin de la compatibilité Swing dans mon application.

24
Johnny

Je vais donner ici une opinion dissidente. 

Propriétés JavaFX et JPA

Comme j'ai commenté la réponse de jewelsea, l'utilisation d'un bean basé sur une propriété JavaFX avec JPA est possible tant que vous utilisez "accès de propriété" plutôt que "accès de champ". Le blog post I lié y va plus en détail, mais l'idée de base est que toutes les annotations devraient être sur les méthodes get...() et non sur les champs. Autant que je sache, cela empêche l'utilisation de l'un des modèles de propriété JavaFX en lecture seule avec JPA, mais je ne me suis jamais vraiment senti que JPA fonctionnait bien avec des propriétés en lecture seule (c'est-à-dire des méthodes get et no method) .

Sérialisation

Contrairement à mon commentaire sur la réponse de jewelsea, et avec l'avantage de quelques semaines pour travailler avec cela (et étant placé dans une position où je devais répliquer plusieurs classes d'entités côté client JavaFX à l'aide de propriétés JavaFX), je pense que le manque de sérialisation des propriétés JavaFX peuvent être contournés. L'observation clé est que vous n'avez vraiment besoin que de sérialiser l'état encapsulé de la propriété (pas, par exemple, les écouteurs). Vous pouvez le faire en implémentant Java.io.Externalizable. Externalizable est une sous-interface de Serializable qui nécessite de renseigner les méthodes readExternal(...) et writeExternal(...). Ces méthodes peuvent être implémentées pour externaliser uniquement l'état encapsulé par la propriété, plutôt que la propriété elle-même. Cela signifie que si votre entité est sérialisée puis désérialisée, vous vous retrouvez avec une nouvelle instance de propriété et tous les écouteurs ne sont pas conservés (c.-à-d. Que les écouteurs deviennent effectivement transient), mais, à ma connaissance, ce serait ce qui était voulu dans tout cas d'utilisation raisonnable.

J'ai expérimenté les haricots définis de cette façon et tout semble bien fonctionner. De plus, j'ai effectué une petite expérience en les transférant entre le client et un service Web reposant, en utilisant le mappeur de Jackson pour la conversion vers et depuis une représentation JSON. Étant donné que le mappeur utilise uniquement les méthodes get et set, cela fonctionne très bien.

Quelques mises en garde

Quelques points doivent être observés. Comme pour toute sérialisation, il est important d'avoir un constructeur sans argument. Et bien sûr, toutes les valeurs entourées par les propriétés JavaFX doivent elles-mêmes être sérialisables - là encore, c'est la même règle que pour tout bean sérialisable.

Le problème des propriétés de JavaFX via les effets secondaires est bien pris en compte, et vous devez faire attention lorsque vous déplacez ces propriétés (qui sont, dans une certaine mesure, conçues avec un modèle mono-threadé à l’esprit) vers un serveur potentiellement multi-threadé. Une bonne règle est probablement que si vous utilisez cette stratégie, les écouteurs ne doivent être enregistrés que du côté client (rappelez-vous que ces écouteurs sont transitoires en ce qui concerne le transfert vers le serveur, que ce soit par sérialisation ou par représentation JSON). Bien entendu, cela suggère que leur utilisation côté serveur pourrait alors être une mauvaise conception; cela devient un compromis entre la commodité d'avoir une seule entité qui est "tout pour tout le monde" (propriétés observables pour le client JavaFX, sérialisable pour la persistance et/ou l'accès à distance et avec les mappages persistants pour JPA) par rapport à la fonctionnalité d'exposition (par exemple, observabilité) où cela peut ne pas être totalement approprié (sur le serveur).

Enfin, si vous utilisez les annotations JPA, celles-ci ont une rétention à l'exécution, ce qui implique (je pense) que votre client JavaFX aura besoin de la spécification javax.persistence sur le chemin d'accès aux classes).

Voici un exemple d'une telle entité "homme pour toutes les saisons":

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());
    }

}
18
James_D

Conception de la propriété JavaFX

Les propriétés JavaFX sont conçues de sorte qu'il n'est pas nécessaire d'exécuter un programme JavaFX pour les utiliser. Les sections du Oracle Using JavaFX Properties and Binding Tutorial illustrent une telle utilisation (par exemple, une classe Bill pour modéliser les propriétés d'une facture). L'exemple du tutoriel exécute simplement un programme Java standard avec une variable main et non une application JavaFX . Vous pouvez donc utiliser de manière générique les propriétés et les liaisons sans imposer d'exigences supplémentaires au runtime JavaFX. Cela signifie que vous pouvez par exemple utiliser les propriétés et les liaisons JavaFX dans une application côté serveur.

Pratiques "correctes"

OK, vous pouvez donc le faire, mais est-ce une pratique "correcte"? 

Je ne pense pas que beaucoup de gens utilisent les propriétés JavaFX de cette manière. Une des raisons est tout simplement parce que les propriétés JavaFX sont relativement nouvelles. Je ne pense pas que ce soit "faux" d'utiliser les propriétés JavaFX dans des objets de modèle.

Mises en garde

Les propriétés JavaFX ne prennent pas en charge la sérialisation Java (j'entends par là une prise en charge directe de l'interface Serializable ). De nombreuses technologies Java côté serveur peuvent nécessiter une sérialisation de modèle et ne seraient pas en mesure de sérialiser un objet utilisant les propriétés JavaFX.

Les propriétés JavaFX elles-mêmes ne sont pas sensibles au conteneur et fonctionnent via des effets secondaires (par exemple, changer une propriété peut déclencher une mise à jour vers une autre valeur liée), soyez donc conscient de cela et assurez-vous que ce type de traitement est une approche acceptable dans votre environnement. Veillez notamment à ne pas générer de conditions de concurrence indésirables dans un environnement de serveur multithread (les applications client JavaFX nécessitent généralement moins d'attention à cet égard, car JavaFX s'exécute généralement dans un environnement à un seul thread).

Propriétés JavaFX et Hibernate/JPA

Je ne pense pas que le mélange de propriétés JavaFX dans des classes d'entités Hibernate (ou JPA) soit une bonne idée. Je n'ai vu personne faire cela. Hibernate lui-même n'est pas au courant des propriétés JavaFX et est généralement conçu pour fonctionner avec les primitives Java telles que Strings et ints. Je ne sais donc pas comment il pourrait éventuellement mapper automatiquement une propriété JavaFX sur un champ de base de données. 

Vous auriez probablement besoin d'une configuration définissant votre hiérarchie de classes d'entités et une hiérarchie parallèle pour vos classes de modèle basées sur les propriétés JavaFX et enfin d'une couche de mappeur mappée entre les deux. Ce type de configuration architecturale est essentiellement un modèle MVVM . Le modèle (M) est votre classe d'entité Hibernate et le modèle de vue (VM) est votre modèle basé sur une propriété JavaFX.

9
jewelsea

pour les collections jpa persistantes, utilisez simplement cette signature pour la méthode getter.

@OneToMany
List<Person> getAll()
{
return observableList;
}
0
user2768310