1

Given the definitions (ids omitted for simplicity):

@Entity
class A {
   @OneToMany(mappedBy="a", cascade=CascadeType.ALL)
   B b;
}

@Entity    
class B {
   @ManyToOne
   @JoinColumn(nullable=false)
   A a;
}

And the statements:

a.setB(b);
b.setA(a);
session.update(a);
session.flush();

We get PropertyValueException ("not-null property references a null or transient value") in the flush. But if we swap "a.setB(b)" with "b.setA(a)" no exception is thrown. It's as if "a.setB(b)" is firing an sql update with a null value in "b.a", regardless of the next setter and update lines.

We weren't getting this behavior before, it apparently started after moving from hibernate v3.6 to v4.3. How does hibernate decide to generate sql updates according to entity state changes or method calls in the entities and the session? Is there a configuration I can set to change it to the previous behavior?

Note: These statements are simplified, there's more code in between them.

Victor Basso
  • 5,556
  • 5
  • 42
  • 60
  • There are some auto flush settings... I can't remember now, but I've always done flush after each DML query. – Danubian Sailor Sep 22 '14 at 13:25
  • My flushMode was AUTO. I tried changing to COMMIT but the problem is still there. – Victor Basso Sep 22 '14 at 19:21
  • Actually I had set flushMode in "openSessionInViewFilter" inside web.xml (http://stackoverflow.com/a/13362558/2004857) which didn't work. This time I did it programmatically after sessionFactory.getCurrentSession() and the exception didn't throw. – Victor Basso Sep 22 '14 at 21:25

2 Answers2

1

Hibernate won't fire a query after the setter itself but after a flush.

In my case a flush was being triggered by a read operation after "a.setB(b)" (while the entity was in an inconsistent state: b.a == null) because I had flushMode set to AUTO in hibernate.

I had two ways of fixing it:

  1. Moving the read operation to after "b.setA(a)", when the state is consistent; or
  2. Setting flushMode to COMMIT. Note that you now have to manually flush before every db operation involving entities updated earlier in the same transaction or you might get stale data.

    Session session = sessionFactory.getCurrentSession();
    session.setFlushMode(FlushMode.COMMIT);
    
Community
  • 1
  • 1
Victor Basso
  • 5,556
  • 5
  • 42
  • 60
  • 1
    I am trying to understand how you fixed this issue, What kind of relation is there between A & B? Is it One-to-One? What do you mean by the word `find` in your answer at `In my case a flush was being triggered by a find after` and `Moving the find to after` and ` Note that you now have to manually flush before every find `? as there is no find method in hibernate session interface. How the stale data came into picture in this scenario? can you please elaborate. – Chaitanya Sep 23 '14 at 05:09
  • 1
    You're totally right. I've added the annotations defining the relationship, it will make more sense now. By "find" I mean "read operation". About flushMode and stale data, I think this explains it well: http://stackoverflow.com/a/7222097/2004857. Thanks for pointing these things out. – Victor Basso Sep 23 '14 at 12:32
0

I think you're getting this because of the "or transient value". Make sure you fetched the instance of B properly from the database or that you told Hibernate to save it as well (either with an explicit call to persist()/update()) or by creating a cascading relation

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820