Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using javafx.beans properties in model classes

Is it a correct practice to use JavaFX beans properties in the model classes?

I wonder if it is a good practice to use properties in model classes to be able to bind them easier with the view components. I'm not worried about the availability of those libraries in future because my program will be run on JRE8 or later but the nature of using the JavaFX libraries in the model classes make me skeptical and I'm worried about current and future incompabilities specially because I want to use Hibernate to persist those attributes.

Note: I use a pure JavaFX environment and I will never need Swing compability in my application.

like image 950
Johnny Avatar asked Apr 20 '14 21:04

Johnny


People also ask

What is JavaFX beans property?

property Description. The package javafx. beans. property defines read-only properties and writable properties, plus a number of implementations.

Are JavaFX properties thread safe?

The JavaFX scene graph, which represents the graphical user interface of a JavaFX application, is not thread-safe and can only be accessed and modified from the UI thread also known as the JavaFX Application thread.

What is Property binding in JavaFX?

JavaFX has a binding API, which provides ways of binding one property to the other. This means that whenever one property's value is changed, the value of the bound property is updated automatically.

What is double property JavaFX?

public static DoubleProperty doubleProperty(Property<Double> property) Returns a DoubleProperty that wraps a Property and is bidirectionally bound to it. Changing this property will result in a change of the original property. This is very useful when bidirectionally binding an ObjectProperty and a DoubleProperty.


2 Answers

I'm going to offer something of a dissenting opinion here.

JavaFX Properties and JPA

As I commented on jewelsea's answer, using a JavaFX property-based bean with JPA is possible as long as you use "property access" rather than "field access". The blog post I linked there goes into more detail on this, but the basic idea is that any annotations should be on the get...() methods and not on the fields. As far as I can see, this does prevent use of any of the read-only JavaFX property patterns in conjunction with JPA, but I've never really felt JPA played well with read only properties (i.e. get methods and no set method) anyway.

Serialization

Contrary to my comment on jewelsea's answer, and with the benefit of a few weeks to work with this (and having been placed in a position where I was facing replicating several entity classes on a JavaFX client side using JavaFX properties), I think the lack of serialization of JavaFX properties can be worked around. The key observation is that you really only need to serialize the wrapped state of the property (not, for example, any listeners). You can do this by implementing java.io.Externalizable. Externalizable is a sub-interface of Serializable that requires you to fill in the readExternal(...) and writeExternal(...) methods. These methods can be implemented to externalize just the state wrapped by the property, rather than the property itself. This means that if your entity is serialized and then deserialized, you will end up with a new property instance, and any listeners will not be preserved (i.e. the listeners effectively become transient), but as far as I can see this would be what was wanted in any reasonable use case.

I experimented with beans defined this way and it all seems to work nicely. Additionally, I ran a small experiment in transferring them between the client and a restful web service, using the Jackson mapper to convert to and from a JSON representation. Since the mapper just relies on using the get and set methods, this works just fine.

Some caveats

A couple of points need to be observed. As with any Serialization, it's important to have a no-argument constructor. And of course, all values wrapped by the JavaFX properties must themselves be serializable - again this is the same rule as for any serializable bean.

The point about JavaFX properties working via side-effects is well taken, and care needs to be exercised when moving these properties (which are, to some extent, designed with a single-threaded model in mind) to a potentially multi-threaded server. A good rule of thumb is probably that if you use this strategy, listeners should only be registered on the client side (and remember, those listeners are transient with respect to transfer back to the server, whether by serialization or by JSON representation). Of course, that suggests that using these on the server side then might be a bad design; it becomes a trade-off between the convenience of having a single entity that is "all things to all people" (observable properties for the JavaFX client, serializable for persistence and/or remote access, and with persistent mappings for JPA) versus exposing functionality (e.g. observability) where it might not be fully appropriate (on the server).

Finally, if you do use the JPA annotations, those have runtime retention which implies (I think) that your JavaFX client will need the a javax.persistence specification on the classpath).

Here's an example of such a "man for all seasons" entity:

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

}
like image 51
James_D Avatar answered Oct 17 '22 15:10

James_D


JavaFX Property Design

JavaFX properties are designed such that you don't need to be running a JavaFX program to use them. The sections of the Oracle Using JavaFX Properties and Binding Tutorial, demonstrates such usage (e.g. a Bill class to model the properties of a bill). The sample from the tutorial just runs a standard Java program with a main and not a JavaFX Application. So you can generically use properties and binding without having an additional requirement on the JavaFX runtime. This means that you could for instance, make use of JavaFX properties and bindings in a server side application.

"Correct" Practices

OK, so you can do it, but is it "correct" practice?

I don't think many people use JavaFX properties in this way. One reason for this is simply because JavaFX properties are quite new. I don't think it's "wrong" to use JavaFX properties in model objects.

Caveats

JavaFX properties do not support Java serialization (by this I mean direct support for the Serializable interface). Numerous server side Java technologies might require model serialization and they would be unable to serialize any object which made use of JavaFX properties.

JavaFX properties themselves are not container aware and work via side-effects (e.g changing a property may trigger an update to another bound value), so be aware of this and make sure that this kind of processing is an acceptable approach in your environment. In particular, be careful you do not generate unwanted race conditions in a multi-threaded server environment (JavaFX client applications generally require less care with respect to this as JavaFX in general runs mostly as a single threaded environment).

JavaFX Properties and Hibernate/JPA

I don't think mixing JavaFX properties into Hibernate (or JPA) entity classes is a good idea. I have not seen anybody do that. Hibernate itself is not aware of JavaFX properties and in general is designed to work against Java primitives like Strings and ints, so I don't know how it could possibly automatically map a JavaFX property to a database field.

You would likely need a setup which defined your entity class hierarchy and a parallel hierarchy for your JavaFX property based model classes and finally a mapper layer which mapped between the two. This kind of architectural setup is essentially an MVVM model. The model (M) is your Hibernate entity classes, and the view model (VM) is your JavaFX property based model.

like image 44
jewelsea Avatar answered Oct 17 '22 15:10

jewelsea