Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 331953

Summary: [Hibernate] Roll back entity identifiers after unsuccessful resource save
Product: [Modeling] EMF Reporter: Aleksander Bandelj <aleksander.bandelj>
Component: TeneoAssignee: Martin Taal <mtaal>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: P3    
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Whiteboard:

Description Aleksander Bandelj CLA 2010-12-06 16:00:14 EST
Build Identifier: 

Let's consider saving of resource that fails because of database constraint or trigger. Hibernate makes no attempt to synchronize database and session in this case, so there are objects with assigned identifiers which are considered saved by Hibernate, but are not present in database. This leads to StaleObjectExceptions later on. It would be nice to add some form of resynchronization to teneo resource. 

My working implementation modifies finally clause in HibernateResource#saveResource (override points are useful for other things as well)

			if (!hasSessionController) {
				if (err) {
					beforeSaveRollback();
					mySessionWrapper.rollbackTransaction();
					afterSaveRollback();
					// see bugzilla 221950
					// mySessionWrapper.close();
				} else {
					mySessionWrapper.commitTransaction();
				}
			}

And then (seems slighlty awkward):

	protected void beforeSaveRollback() {
		// assigned ids
		Session session= sessionWrapper.getHibernateSession();
		for (EObject eobject : super.getContents()) {
			String entityName= session.getEntityName(eobject);
			String identifierName= getIdentifierName(eobject, session);
			Serializable id= session.getIdentifier(eobject);
			Criteria exists= session.createCriteria(entityName).add(Restrictions.eq(identifierName, id));
			if(exists.uniqueResult() == null) {
				rollbackID(eobject, identifierName);					
			}
		}	
	}

	protected void afterSaveRollback() {
// not necessary
//		sessionWrapper.clear();
//		sessionWrapper.close();
//		sessionWrapper = null;
	}
		
	protected void rollbackID(EObject eobject, String identifierName) {
		if(identifierName == null)
			return;
		EStructuralFeature identifier= eobject.eClass().getEStructuralFeature(identifierName);
		if(identifier == null)
			return;
		eobject.eUnset(identifier);
		IdentifierCacheHandler.getInstance().setID(eobject, null);		
	}

	private String getIdentifierName(EObject eobject, Session hs) {
		String entityName= hs.getEntityName(eobject);
		if(entityName == null)
			return null;
		ClassMetadata entityMetaData= hs.getSessionFactory().getClassMetadata(entityName);
		if(entityMetaData == null)
			return null;
		String identifierName= entityMetaData.getIdentifierPropertyName();
		return identifierName;
	}

Reproducible: Always
Comment 1 Martin Taal CLA 2011-02-21 02:03:39 EST
I applied your changes in the latest build. Instead of iterating over all eobjects I only iterate over the new eobjects. Can you check if it works for you?