JavaFX properties in JPA

Turn JavaFX domain objects into persistent entities

Originally posted on May 31, 2013
Updated on January 20, 2015

Introduction

This post tries to find an answer to the following question: can I use JavaFX properties in domain classes that have to be persisted using JPA? That is: can I build domain classes that use JavaFX properties instead of regular properties, yet are persistable like regular JPA entity classes? After a bit of trial and error, I came up with the following answer: it's not that hard, if you understand JPA's property access.

Property access vs. field access

JPA has two ways of accessing persistent attributes:

  1. Field access: reading and writing attributes directly through reflection.
  2. Property access: reading and writing attributes through their accessor (getter and setter) methods.

If you do not explicity set an access type, JPA will figure out what to do:

  • If you annotate your fields, JPA will use field access.
  • If you annotate your getter methods, JPA will use property access.
  • If you annotate both fields and getter methods, you're asking for trouble.

If you want to use property access, do the following:

  • Do not annotate any fields.
  • Provide both a getter and a setter for each property you want to persist. These methods have to be either protected or public.
  • Place all annotations on the getter methods.

A simple example

The following example shows how to map simple Employee and Department classes, including a unidirectional many-to-one relationship between them. The resulting database schema is exactly the same as if you had used regular properties.

@Entity
public class Department {
    
    private final StringProperty name = new SimpleStringProperty();

    @Id
    public String getName() {
        return name.get();
    }

    public void setName(String value) {
        name.set(value);
    }

    public StringProperty nameProperty() {
        return name;
    }
}

@Entity
public class Employee {
    
    private final IntegerProperty id = new SimpleIntegerProperty();

    @Id @GeneratedValue
    public int getId() {
        return id.get();
    }

    public void setId(int value) {
        id.set(value);
    }

    public IntegerProperty idProperty() {
        return id;
    }
    
    private final StringProperty name = new SimpleStringProperty();

    public String getName() {
        return name.get();
    }

    public void setName(String value) {
        name.set(value);
    }

    public StringProperty nameProperty() {
        return name;
    }
    
    private final ObjectProperty<Department> department = new SimpleObjectProperty<>();

    @ManyToOne
    public Department getDepartment() {
        return department.get();
    }

    public void setDepartment(Department value) {
        department.set(value);
    }

    public ObjectProperty<Department> departmentProperty() {
        return department;
    }
}

Mixing access types

If you only want a few JavaFX properties in your entity classes, you could go for mixed access types. For example:

@Entity
@Access(AccessType.FIELD)
public class Employee {
    
    @Id @GeneratedValue
    private int id;
    
    @Transient
    private final StringProperty name = new SimpleStringProperty();

    @Access(AccessType.PROPERTY)
    public String getName() {
        return name.get();
    }

    public void setName(String value) {
        name.set(value);
    }

    public StringProperty nameProperty() {
        return name;
    }
    
    @ManyToOne
    private Department department;

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}

Note that:

  • the default access type for this class is explicity set to field access.
  • the access type for the name property is changed from the default to property access.
  • the underlying StringProperty attribute is marked as transient, otherwise JPA would persist both the property and the field.

Update: James Denvir has more on this on his blog.