Showing posts with label hibernate. Show all posts
Showing posts with label hibernate. Show all posts

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!

Wednesday, May 7, 2014

Eclipse with Tomcat Server: Whither Context.xml in Tomcat Connection Pooling

I'm implementing a web application with the following configuration:

  • Java 1.7
  • Hibernate 4
  • Tomcat 7
  • MySQL
Hibernate declares quite plainly that developers should avoid using its own connection pooling implementation for production systems.  The dearth of good documentation could paper a welcome mat.  Tomcat doesnt do much better.  

What's more, if you're using Eclipse to build the application and you want to debug on a tomcat server, how do you connect to MySQL without using Hibernate's connection pooling in hibernate.cfg.xml?  I cant provide the entire answer, but here's an important point for configuration:

The tomcat server in your Eclipse IDE has its own context.xml.  This is the file where you identify the database and its credentials to use by your applications.  If you assumed that the context.xml file located in your application's META-INF would be exercised, you'd be dead wrong.

Apparently, when you run your app in Eclipse, the attached Tomcat server only recognizes the context.xml file it has, and it ignores the one in the application.  Painful.

So do yourself a favor and add your database connection information into the server's context.xml file or feel the wrath of "org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'".