I am using Spring with hibernate OGM & mongo db. The use case is simple -- employee & department association . One department can have many employees but one employee can be associated with one department. Entity objects are as follows :
Code:
@Entity
@Table(name="ut_department"
, uniqueConstraints=@UniqueConstraint(columnNames={"tenant_id", "dept_code"})
)
@Cacheable(true)
//...
public class DepartmentBean implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@DocumentId
@Column(name="department_id", nullable=false)
private Long id;
@Version
@Column(name="opt_lock")
private Long version;
@OneToMany(cascade={CascadeType.PERSIST})
@JoinColumn(name="employee_id")
@ContainedIn
private List<EmployeeBean> employees = new ArrayList<>();
// ... other fields
// Getters & Setters omitted for brevity
}
-------------
Code:
@Entity
@Table(name="ut_employee"
, uniqueConstraints=@UniqueConstraint(columnNames={"tenant_id", "employee_number"})
)
@Cacheable(true)
// ...
public class EmployeeBean implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="employee_id", nullable=false)
@DocumentId
private Long id;
@Version
@Column(name="opt_lock")
private Long version;
@ManyToOne(cascade={CascadeType.PERSIST})
@IndexedEmbedded
private DepartmentBean currentDept;
// ... other fields
// Getters & Setters omitted for brevity
}
----------------
DAO layer implemented using Spring repository concept ... with Spring's declarative transaction management --
Code:
@Repository
public class EmployeeRepositoryImpl extends AbstractRepositoryImpl<EmployeeBean, Long> implements EmployeeRepository {
@Override
@Transactional
public EmployeeBean createEmployee(EmployeeBean employee, DepartmentBean dept) {
EntityManager em = getEntityManager(); // EntityManager is injected by Spring
em.persist(employee);
if (dept != null) {
if (em.contains(dept)) {
employee.setCurrentDept(dept);
dept.getEmployees().add(employee);
} else {
// *** control comes here as dept is detached when called from Service layer
DepartmentBean dept2 = em.merge(dept);
employee.setCurrentDept(dept2);
dept2.getEmployees().add(employee);
}
}
return(employee); // exception is thrown here
}
....
}
----------------
The service class is as follows ---
Code:
@Service
public class EmployeeService extends AbstractServiceImpl<EmployeeBean, Long> {
@Autowired
private EmployeeRepository empRepo;
@Autowired
private DepartmentRepository deptRepo;
public EmployeeBean add(EmployeeBean entity) {
Long id = null;
if (entity != null) {
// preProcess validates that the department id / code is present
// and gets the complete department bean from db using DepartmentRepository
Outcome result = preProcess(ADD, entity);
if (result.isResult()) {
DepartmentBean dept = entity.getCurrentDept();
entity.setCurrentDept(null);
empRepo.createEmployee(entity, dept);
Outcome result2 = postProcess(ADD, entity);
if (!result2.isResult()) {
throw new EntityValidationException("Post-process Error - " + result2.getErrorMessage());
}
} else {
throw new EntityValidationException("Pre-process Error - " + result.getErrorMessage());
}
}
return(entity);
}
.....
}
------------
The exception stack from the DAO layer is as follows :
Code:
org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.olp.jpa.module.docu.ut.EmployeeBean] with identifier [598]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.olp.jpa.module.docu.ut.EmployeeBean#598]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:294)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:221)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:521)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy83.createEmployee(Unknown Source)
at com.olp.jpa.module.docu.ut.EmployeeService.add(EmployeeService.java:200)
at com.olp.jpa.module.docu.ut.EmployeeService.addAll(EmployeeService.java:227)
at com.olp.jpa.common.DataLoader.load(DataLoader.java:55)
at com.olp.jpa.module.docu.ut.PreTest.loadTestData(PreTest.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:198)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:274)
at org.springframework.test.context.junit4.SpringMethodRoadie$2.run(SpringMethodRoadie.java:207)
at org.springframework.test.context.junit4.SpringMethodRoadie.runBeforesThenTestThenAfters(SpringMethodRoadie.java:254)
at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:234)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:204)
at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:146)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:151)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:234)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:133)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:188)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:166)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:86)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:101)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:74)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.olp.jpa.module.docu.ut.EmployeeBean#598]
at org.hibernate.ogm.persister.impl.OgmEntityPersister.raiseStaleObjectStateException(OgmEntityPersister.java:1739)
at org.hibernate.ogm.persister.impl.OgmEntityPersister.update(OgmEntityPersister.java:1125)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.ogm.dialect.eventstate.impl.EventContextManagingFlushEventListener.onFlush(EventContextManagingFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.ogm.transaction.impl.EmulatedLocalTransaction.beforeTransactionCommit(EmulatedLocalTransaction.java:55)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
... 41 more
Tried many options but no break through !! Any pointers will be very much appreciated.
Thanks,
Rajesh