Here is the solution that works for me (Serialize SimpleXXXProperty on a JPA Entity where xxx can be String, Object, etc)
https://gist.github.com/james-d/a7202039b00170256293
You 'just' have to :
1) implements Serializable
2) set all Properties as transient
3) define 2 specials methodes like that :
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeLong(idProperty().longValue());
s.writeUTF(aStringProperty().getValueSafe()); // can't be null so use getValueSafe that returns empty string if it's null
s.writeUTF(anOtherStringPoperty().getValueSafe());
}
and
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
idProperty.set(s.readLong());
aStringProperty.set(s.readUTF());
anOtherStringPoperty(s.readUTF());
// set values in the same order as writeObject()
}
and voilà :) You can serialize your Object with
MyObject o = new MyObject();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
Note that these strange private methodes are directly called by the Java Virtual Machine and must not be Override, public or other as mentionned in the official documentation.
Edited : don't use writeChars for String but writeUTF.
And remember that on deserialization process, the "readed object" isn't instanciated (constructor isn't called!) and then all Properties aren't initialized and so idProperty.set(o) will throws nullPointerException.
So you have to create a initMethode where all properties are initialized. Call this methode from your constructor AND from readObject methode before read data from the ObjectInputStream
Edit : I made helpers for automatic write and read. Here is code if you want to use it :
public class WriteObjectsHelper {
// write a StringProperty to ObjectOutputStream
public static void writeStringProp(ObjectOutputStream s, StringProperty strProp) throws IOException {
s.writeUTF(strProp.getValueSafe());
}
// write a ListProperty to ObjectOutputStream
public static void writeListProp(ObjectOutputStream s, ListProperty lstProp) throws IOException {
if(lstProp==null || lstProp.getValue()==null) {
s.writeInt(0);
return;
}
s.writeInt(lstProp.size());
for(Object elt:lstProp.getValue()) s.writeObject(elt);
}
// automatic write set of properties to ObjectOutputStream
public static void writeAllProp(ObjectOutputStream s, Property... properties) throws IOException {
s.defaultWriteObject();
for(Property prop:properties) {
if(prop instanceof IntegerProperty) s.writeInt(((IntegerProperty) prop).intValue());
else if(prop instanceof LongProperty) s.writeLong(((LongProperty) prop).longValue());
else if(prop instanceof StringProperty) s.writeUTF(((StringProperty)prop).getValueSafe());
else if(prop instanceof BooleanProperty) s.writeBoolean(((BooleanProperty)prop).get());
else if(prop instanceof ListProperty) writeListProp(s,(ListProperty)prop);
else if(prop instanceof ObjectProperty) s.writeObject(((ObjectProperty) prop).get());
else throw new RuntimeException("Type d'objet incompatible : " + prop.toString());
}
}
}
public class ReadObjectsHelper {
// Read a ListProperty from ObjectInputStream (and return it)
public static ListProperty readListProp(ObjectInputStream s) throws IOException, ClassNotFoundException {
ListProperty lst=new SimpleListProperty(FXCollections.observableArrayList());
int loop=s.readInt();
for(int i = 0;i<loop;i++) {
lst.add(s.readObject());
}
return lst;
}
// automatic fill a set of properties with values contained in ObjectInputStream
public static void readAllProp(ObjectInputStream s, Property... properties) throws IOException, ClassNotFoundException {
for(Property prop:properties) {
if(prop instanceof IntegerProperty) ((IntegerProperty)prop).setValue(s.readInt());
else if(prop instanceof LongProperty) ((LongProperty)prop).setValue(s.readLong());
else if(prop instanceof StringProperty) ((StringProperty)prop).setValue(s.readUTF());
else if(prop instanceof BooleanProperty) ((BooleanProperty)prop).setValue(s.readBoolean());
else if(prop instanceof ListProperty) ((ListProperty)prop).setValue(readListProp(s));
else if(prop instanceof ObjectProperty) ((ObjectProperty)prop).setValue(s.readObject());
else throw new RuntimeException("Unsupported object type : " + prop==null?null:prop.toString());
}
}
}
And them, here is part of my (french) JPA entity :
@Entity
@Table(name="articles")
public class Article implements Serializable, IEntity {
private transient LongProperty idArticle;
public LongProperty idArticleProperty() { return idArticle; }
private transient StringProperty descriptionFr;
public StringProperty descriptionFrProperty() { return descriptionFr; }
private transient StringProperty reference;
public StringProperty referenceProperty() { return reference; }
private transient StringProperty constructeur;
public StringProperty constructeurProperty() { return constructeur; }
private transient StringProperty numSAP;
public StringProperty numSAPProperty() { return numSAP; }
private transient StringProperty descriptionEn;
public StringProperty descriptionEnProperty() { return descriptionEn; }
private transient ListProperty<Article> testList;
public ListProperty<Article> articlesProperty() {return testList; }
public Article() {
initInstance();
}
/**
* Need for calling by readObject;
*/
private void initInstance() {
idArticle=new SimpleLongProperty();
descriptionFr=new SimpleStringProperty();
descriptionEn=new SimpleStringProperty();
reference=new SimpleStringProperty();
constructeur=new SimpleStringProperty();
numSAP=new SimpleStringProperty();
testList=new SimpleListProperty<>(FXCollections.observableArrayList());
}
private void writeObject(ObjectOutputStream s) throws IOException {
WriteObjectsHelper.writeAllProp(s,idArticle,descriptionFr,reference,constructeur,numSAP,descriptionEn,testList);
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
initInstance();
ReadObjectsHelper.readAllProp(s, idArticle,descriptionFr,reference,constructeur,numSAP,descriptionEn,testList);
}
@Column(name = "idArticle")
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getIdArticle() {
return idArticle.get();
}
public void setIdArticle(Long idArticle) {
this.idArticle.set(idArticle);
}
//...
}