I'm trying to persist a set of what I would call relatively simple objects to database. Unfortunately, Hibernate seems to be ignoring the @Transient annotation (or it tries to serialize, even WITH the annotation) on several of my classes. This causes a nasty error when I have classes that can't be serialized.
If need be, I can make the AxisEventHandler class serializable, however if I couldn't (eg it was a final class in a closed source library), what would I do then?
I can provide more information on GenericEventHandler class or any of the code I've commented out for brevity on request (but I don't think they're particularly important... I could be wrong).
Exception:
org.hibernate.type.SerializationException: could not serialize
at org.hibernate.util.SerializationHelper.serialize(SerializationHelper.java:139)
at org.hibernate.util.SerializationHelper.serialize(SerializationHelper.java:164)
at org.hibernate.util.SerializationHelper.clone(SerializationHelper.java:95)
at org.hibernate.type.descriptor.java.SerializableTypeDescriptor$SerializableMutabilityPlan.deepCopyNotNull(SerializableTypeDescriptor.java:54)
at org.hibernate.type.descriptor.java.SerializableTypeDescriptor$SerializableMutabilityPlan.deepCopyNotNull(SerializableTypeDescriptor.java:42)
at org.hibernate.type.descriptor.java.MutableMutabilityPlan.deepCopy(MutableMutabilityPlan.java:58)
at org.hibernate.type.AbstractStandardBasicType.deepCopy(AbstractStandardBasicType.java:314)
at org.hibernate.type.AbstractStandardBasicType.deepCopy(AbstractStandardBasicType.java:310)
at org.hibernate.type.TypeHelper.deepCopy(TypeHelper.java:68)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:302)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:713)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:701)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:697)
at com.mypandafinance.amino.gui.ccsimple.CCSimpleApp.initApplication(CCSimpleApp.java:55)
at com.mypandafinance.amino.AminoBoot.main(AminoBoot.java:101)
Caused by: java.io.NotSerializableException: com.mypandafinance.chartcomponent.eventhandlers.AxisEventHandler
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at org.hibernate.util.SerializationHelper.serialize(SerializationHelper.java:135)
... 21 more
Main Axis class:
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Axis implements Serializable {
private int axisId;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getAxisId() { return axisId; }
protected void setAxisId(int id) { this.axisId = id; }
@Transient
public abstract long getMax();
public abstract void setMax(long max);
@Transient
public abstract long getMin();
public abstract void setMin(long min);
/* ... helper methods here */
@Transient
public abstract AxisEventHandler getEventHandler();
@Transient
public abstract AxisFlagHandler getFlagHandler();
@Transient
public abstract TickMarkFormatter getTickMarkFormatter();
public abstract void setTickMarkFormatter(TickMarkFormatter formatter);
}
Basic implementation:
@Entity
@SecondaryTable(name="BasicAxis")
public class BasicAxis extends Axis {
private long min;
private long max;
// Event handler, used for firing off events to all listeners
private AxisEventHandler eventHandler = new AxisEventHandler();
// Flag handler, used for storing axis flags
private SerializableAxisFlagHandler flagHandler;
private TickMarkFormatter formatter;
/* ... constructors */
@Column(name="rangemax")
@Override public long getMax() { return max; }
@Override public void setMax(long max) { /* ... */ }
@Column(name="rangemin")
@Override public long getMin() { return min; }
@Override public void setMin(long min) { /* ... */ }
@Override public void setRange(long min, long max) { /* ... */ }
@Override public void pan(long amount) { /* ... */ }
@Transient
@Override public AxisEventHandler getEventHandler() { /* ... */ }
@Type(type="com.mypandafinance.chartcomponent.hibernateusertypes.AxisFlagHandlerUserType")
@Column(name="flags")
@Override public SerializableAxisFlagHandler getFlagHandler() { /* ... */ }
protected void setFlagHandler(SerializableAxisFlagHandler flagHandler) { /* ... */ }
@Column(name="formatter", table="BasicAxis")
@Override public TickMarkFormatter getTickMarkFormatter() { /* ... */ }
@Override public void setTickMarkFormatter(TickMarkFormatter formatter) { /* ... */ }
/* ... helper methods */
}
AxisEventHandler class:
public class AxisEventHandler extends GenericEventHandler<AxisEventListener> {
public AxisEventHandler() { super(new AxisEventListener[0]); }
}
Never mind, I seem to have made a mistake that, in retrospect, should not have taken me several hours to figure out. The problem was not with hibernate, or anything to do with Hibernate (other than that Hibernate was using standard java serialization).
Not only do the fields need to be marked with JPA @Transient annotation, but they also need to be transient for the Serializable interface (because, for some reason Hibernate serializes the objects before persisting them?)
Fix:
public class BasicAxis extends Axis {
/* ... */
private transient AxisEventHandler eventHandler = new AxisEventHandler();
/* ... */
}