Friday, August 7, 2015

Hibernate Red Herring #2315: @Version and the Transient Object Exception

I have a class, PitchingStat, that looks like this:

@Entity
@Table(name="PitchingStat", schema="hotDogs")
public class PitchingStat{

@Id
@Column(name="id")
private long id;

@Column(name="player_id", nullable=false)
private int playerId;

@Column(name="strikeOuts")
private int strikeOuts;

@ManyToOne
@JoinColumn(name="player_id", referencedColumnName="player_id")
private MLBPlayer mlbPlayer

.
.
.
}

It has a parent class nested in it, MLBPlayer.

@Entity
@Table(name="MLBPlayer", schema="hotDogs")
public class MLBPlayer{

@Id
@Column(name="player_id")
private int playerId;

@Column(name="created")
private Date created;

@Column(name="createdBy")
private String createdBy;

@Version
private long version;

.
.
.
}

If you implemented @Version, as I did, after the MLBPlayer table had been created and populated with data, then it's possible that you have encountered the transient object exception. Much of the MLBPlayer table's version column might have null values in some of the records. While this may seem innocuous at first blush, you obviously haven't worked with this Hibernate gem.

I attempted to save the PitchingStat object like this:

assume this MLBPitchingStat record is linked to an MLBPlayer record that has version = null.
MLBPitchingStat pitchingStat = getMLBPitchingStatByIdFromDb(pitchingSession, Id);

pitchingStat.setStrikeOuts(34);

pitchingSession.saveOrUpdate(pitchingStat);

Wham! Transient Object Exception: You need to save this transient entity, MLBPlayer, prior to saving the current object.

What happened? Apparently Hibernate has a fit over the version column's null value in the database. The version needs to be set in MLBPlayer, prior to saving the child record, MLBPitchingStat. So how do you go about resolving this?

Try this:
MLBPitchingStat pitchingStat = getMLBPitchingStatByIdFromDb(pitchingSession, Id);

pitchingStat.setStrikeOuts(34);

MLBPlayer mlbPlayer = pitchingStat.getMLBPlayer();

mlbPlayer.setVersion(1L);

pitchingStat.setMLBPlayer(mlbPlayer);

pitchingSession.saveOrUpdate(pitchingStat);

Now, you would think this would work. Wrong! Hibernate still has that fit over the version column's null value in the database. So what's the answer?

Do this:
In your database:
Update MLBPlayer as p
set p.version = 1
where p.version is null;

Problem solved.

Woof!