This one has had me stumped for several hours. It's related to my other question here:
JPA/hibernate - Cannot add or update a child row: a foreign key constraint fails - BUT record exists
I got rid of nearly all the code and narrowed down the problem. I have three very simple entities:
@Entity public class Building { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; public Building() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } } Other.java
@Entity public class Other { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne @JoinColumn(name = "building_id") private Building building; public Other() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Building getBuilding() { return building; } public void setBuilding(Building school) { this.building = school; } } Event.java
@Entity public class Event { @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id; @ManyToOne @JoinColumn(name = "other_id") private Other other; public long getId() { return id; } public void setId(long id) { this.id = id; } public Other getOther() { return other; } public void setOther(Other other) { this.other = other; } } This will fail with a foreign key constraint violation:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`blah`.`event`, CONSTRAINT `FK403827A76D0546B` FOREIGN KEY (`other_id`) REFERENCES `Other` (`id`)) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) NOW HERE'S THE REALLY BIZARRE PART!!!! If I do an eclipse refactor and rename the "Other" entity "Bob" it works perfectly fine. Just so we're on the same page, the Event entity will now look like this:
@Entity public class Event { @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id; @ManyToOne @JoinColumn(name = "bob_id") private Bob bob; public long getId() { return id; } public void setId(long id) { this.id = id; } public Bob getBob() { return bob; } public void setBob(Bob bob) { this.bob = bob; } } And now it works perfectly fine. Can someone please explain this to a Hibernate newbie like myself? This really has me stumped. It fails when I persist a new Event to the database in my service layer:
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void addEvent(Event event) throws ErrorCodeException, Exception { // not sure if I need to do this, still fails if I remove it event.setOther(otherDAO.getReferenceById(event.getOther().getId())); eventDAO.persist(event); } One further comment. Instead of renaming Other to Bob, I can also set the table name to lowercase:
@Entity @Table(name = "other") public class Other { This ALSO fixes it. But I cannot accept these solutions. I want to know why it's failing. I made a technology decision not to use Grails to avoid this type of "magic".
Here's the SQL hibernate logs:
// Initial database inserts Hibernate: insert into Building values ( ) Hibernate: insert into Other (building_id) values (?) // Insert which throws exception Hibernate: insert into Event (other_id) values (?) 2011-06-27 11:31:51 JDBCExceptionReporter [WARN] SQL Error: 1452, SQLState: 23000 2011-06-27 11:31:51 JDBCExceptionReporter [ERROR] Cannot add or update a child row: a foreign key constraint fails (`blah`.`event`, CONSTRAINT `FK403827A76D0546B` FOREIGN KEY (`other_id`) REFERENCES `Other` (`id`)) org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: could not insert: [com.blah.server.domain.Event]; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [com.blah.server.domain.Event]