0

We are creating a new web application backed by JPA to replace an old web application. As part of the migration we are converting the old application's database to a new, more sophisticated, JPA-managed database.

So I've written a 'script' that converts the old database to a set of JPA entities and subsequently saves them. It works like this:

  1. Create an order of conversion based on the dependencies of the domain models
  2. For each entity
    1. Execute database query to legacy DB
    2. Store new object for each obtained table row in a list in memory
  3. Iterate over generated lists in the same order as the conversion, and persist each entity.

Now, the first two steps work well. Upon persisting, however I get an exception. The exception occurs when one entity has a relation to another entity. For example if one of our entities would be a Book and another would be Chapter defining a @ManyToOne(optional=false) relation to Book. Upon persisting the Chapter, it throws the exception java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: models.Chapter.book -> models.Book.

Of course, this indicates that something is wrong with the state of the book: it seems it is either not set or has not yet been persisted. However, I can verify that the Book is set properly in the conversion of the Chapter, and I can also verify that all entities of type Book are persisted by the EntityManager before the entities of type Chapter get persisted. Obviously, my JPA provider does not behave as expected and does not truly persist my Book objects for some reason.

What solution would allow me to save the entire graph of objects that I have converted to the database? I use Hibernate as my JPA provider and I also use Spring 3.1 for injection of dependencies and EntityManagers.

EDIT 1: Some additional info: I've again verified that entityManager.persist() is called on each of the book objects before entityManager.persist() is called on the chapters. However, the id of the book object remains null, meaning it is not properly persisted. The database also remains empty, despite not using transactions.

EDIT 2: Because I don't think it's clear from the text above: the Book and Chapter story is just an example. It happens for any entity that references another entity. This makes it seem as if I'm not using JPA/Hibernate properly as opposed to not setting the values of my entities properly.

EDIT 3: The core issue seems to be that despite persisting Book properly, having all the right annotations, book.getId() remains null. Basically, Hibernate is not setting the ids on my entities after persisting them, leading to problems when I need to use those entities later.

2
  • How are you saving your entities? How many entity managers are involved in the process? Commented Jan 19, 2013 at 18:22
  • I'm saving my entities by calling entityManager.persist(entity) on them with an EntityManager obtained by Spring's @PersistenceContext. It's quite a complicated domain model with over 60 different types of entities with all sorts of relations between them. Commented Jan 19, 2013 at 19:10

4 Answers 4

3

I once battled with such an error from hibernate myself. It turned out that it was a combination of a circle in the object graph and the cascade settings that caused the problem.

It has been a while so the fowlling might not be 100% accurate but maybe it is enough information to track your problem:

  1. Hibernate Wants to insert the chapter. Realizes it needs to insert the book first.
  2. Wants to insert the book. Realizes it needs to insert another entity first (e.g. publisher)
  3. Inserts publisher and performs cascades defined on publisher (e.g. authors)
  4. Author has e.g. reference to his lastestBook. Because hibernate internally already marked the book as processed (in step 2) you would no get an exception stating that author.book references a transient instance.

To find out if this is your problem you can enable full hibernate debugging and follow the path hibernate is taking through your object graph.

Sign up to request clarification or add additional context in comments.

2 Comments

Hmm, like my example, this also occurs without having bidirectional relations or any way for an object higher in the hierarchy to cascade. It's a useful tip to turn on full debugging though, I'll give it a try.
I am having this same problem. How do I fix it?
0

I've found the answer thanks to the discussion I've had with user1888440.

The solution to this answer was that the Spring @Transactional annotation was nonfunctional in my application. This mean that everything Hibernate did didn't occur in the context of a transaction. This meant that Hibernate would not set ids after persisting and this meant that all conversions would break down.

The reason why @Transactional did not work is probably because of a fact I did not mention: this script is part of a Play 2.0 (actually 2.1) app and is thus built using SBT. SBT doesn't use a normal Java setup to build an application, but instead uses the Scala compiler to compile Java as well. My guess is that the Scala compile did not work well with the AspectJ that Spring requires to make @Transactional work.

Instead, I performed all of the database work involved in this conversion within a programmatically defined Spring transaction (section 11.6). Now everything behaves as expected.

Comments

0

Check he unsaved values for your primary key/Object ID in your hbm files.If you have automated ID creaion by hibernate framework and you are stting th ID somewhere it woudl throw this error.By defaut the unsaved-value is 0 , so if you set the ID as 0 you would see this error.

Comments

-1

Sounds like you are forgetting to assign a Book to each Chapter before persisting it. Even if you have persisted the Book it needs to be assigned to the #book property of the Chapter instance before you can persist the Chapter. This is because you have specified the relationship as non-optional. #book can never be null.

7 Comments

I've mentioned that I verified that the chapter has a book assigned to it properly. "However, I can verify that the Book is set properly in the conversion of the Chapter, and I can also verify that all entities of type Book are persisted by the EntityManager before the entities of type Chapter get persisted."
Can you post the Book and Chapter entities along with the stack trace of the error? Does Book have any relational annotations to Chapter or just Chapter to Book?
Are you saying that when you step through the code with a debugger that #book is NOT null? If so, what is the condition (stepping through with a debugger) that causes the hibernate code to throw this exception?
The condition that causes Hibernate to throw the exception is not that #book is null, it's the fact that #book.id is null, despite that I have persisted that book before.
Only Chapter references Book. Mind you, I don't actually have literal Chapter and Book entities, I use them just as examples. However, all of my entities are just regular Java beans annotated with @Entity and other annotations on the fields.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.