Scenario: I have a JSF 2.1 webapp running on a tomcat server, with hibernate implementing the jpa interface and mysql database.
Users of the webapp are registered as an Entity on the database, and there are a group of data called Rooms, that have their own Entity, where users have access as proprietary or as external managers. To save the concept of "external management" on the database i have a proper Entity for every management; the external management are saved as a OneToMany relathionship on the User Entity.
Problem: For some odd reason, adding the code for a foreign key to the managed room in the External Management Entity with a ManyToOne relationship, breaks up Hibernate: any update to the list of external management property on the User, when that foreign key is present, throws RollbackException - "Transaction marked as rollback only":
Code:
Caused by: it.katuiros.core.db.dao.DataAccessException: Data Access error while doing final commit.
at it.katuiros.core.db.dao.impl.GenericDaoImpl.closeDataAccess(GenericDaoImpl.java:222)
at it.katuiros.core.db.dao.impl.GenericDaoImpl.update(GenericDaoImpl.java:96)
at it.katuiros.appLogic.PersistenceTest.saveTestData(PersistenceTest.java:157)
at it.katuiros.beans.generics.TestBean.<init>(TestBean.java:44)
... 67 more
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:74)
at it.katuiros.core.db.dao.impl.GenericDaoImpl.closeDataAccess(GenericDaoImpl.java:217)
... 70 more
As you probably know, the sad thing about those RollbackException in Hibernate is that they are UNDEBUGGABLE!
CODEthe User entity:
Code:
@Entity
@Table(name = "USER")
@Access(AccessType.FIELD)
public class User extends DataModelEntity {
/**
* Serialization ID
*/
private static final long serialVersionUID = -1154710247841840471L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Basic(optional = false)
@Column(name = "ID")
protected int id;
@NotNull
@Column(name = "USERNAME", unique = true)
protected String username;
@NotNull
@Column(name = "PASSWORD")
protected String password;
@NotNull
@Column(name = "EMAIL", unique = true)
protected String email;
@Column(name = "FIRST_NAME")
protected String firstName;
@Column(name = "LAST_NAME")
protected String lastName;
@Column(name = "PHONE")
protected String telefono;
@ManyToMany
@LazyCollection(LazyCollectionOption.FALSE)
@Basic(optional = false)
@JoinColumn(name = "FAVORITE_ROOMS", referencedColumnName = "ID", updatable = true)
protected Set<Room> favoriteRooms;
@OneToMany
@LazyCollection(LazyCollectionOption.FALSE)
@Basic(optional = false)
@JoinColumn(name = "REGISTERED_ROOMS", referencedColumnName = "ID", updatable = true)
protected Set<Room> registeredRooms;
@OneToMany
@LazyCollection(LazyCollectionOption.FALSE)
@Basic(optional = true)
@JoinColumn(name = "EXTERNAL_MANAGEMENTS", referencedColumnName = "ID", updatable = true)
protected Set<ExternalRoomManagementPermissions> externalManagements;
@OneToOne
@JoinColumn(name = "AVATAR", referencedColumnName = "ID", updatable = true)
protected Image avatar;
... constructors and getters/setters ...
}
The External Management Entity
Code:
@Entity
@Table(name = "EXTERNAL_ROOM_MANAGEMENT_PERMISSIONS")
@Access(AccessType.FIELD)
public class ExternalRoomManagementPermissions extends DataModelEntity {
/**
* Serialization ID
*/
private static final long serialVersionUID = 7122195658297760351L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Basic(optional = false)
@Column(name = "ID")
private int id;
/* CRIMINAL CODE */
@ManyToOne
@NotNull
@JoinColumn(name = "MANAGED_ROOM", referencedColumnName = "ID")
private Room managedRoom;
/* END OF CRIMINAL CODE */
... constructors and getters/setters ...
}
I will not post the Room Entity code, as it is not so important.
and this is the piece of code where the user with a set of external managements is updated on the database and the exception is thrown:
Code:
this.externalManagement1 = new ExternalRoomManagementPermissions(this.room4);
this.dbAccessor.saveData(this.externalManagement1);
this.externallyManagedRoomsForUser1.add(this.externalManagement1);
this.user1.setRegisteredRooms(this.registeredRoomsForUser1);
this.user2.setRegisteredRooms(this.registeredRoomsForUser2);
userDao.update(this.user1);
userDao.update(this.user2);
this.user1.setExternalManagements(this.externallyManagedRoomsForUser1);
userDao.update(this.user1); // <---- EXPLODING LINE
The last two lines are the critical step: if the External Management Entity has that "criminal code" about the ManyToOne relationship to the managed room, the userDao.update(...) will throw the exception; if the "criminal code" is omitted, the userDao.update(...) will NOT throw the exception.
(The room has already been previously persisted to the database before i use it in that last code segment)
Question: sadly i need that ManyToOne relationship to the managed room, do you know why it breaks the persistence up?
Post-Scriptum: i tried to change the ManyToOne relationship to a OneToOne but it had no effect.