-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Sun Jan 24, 2016 3:02 am 
Beginner
Beginner

Joined: Mon Jul 27, 2015 4:03 am
Posts: 29
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


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Mon Jan 25, 2016 3:44 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1628
Location: Romania
First issue is that the child-side tries to cascade the parent-side:

Code:
@ManyToOne(cascade={CascadeType.PERSIST})
@IndexedEmbedded
private DepartmentBean currentDept;


Cascading should happen from parents to children.

I would simplify this logic a bit. Instead of:

Code:
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);
}


to:

Code:
dept = em.find(DepartmentBean.class, dept.getId());
employee.setCurrentDept(dept);
dept.getEmployees().add(employee);


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Mon Jan 25, 2016 7:50 am 
Beginner
Beginner

Joined: Mon Jul 27, 2015 4:03 am
Posts: 29
Thanks a lot. I'll try the suggestions and post the results.


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Wed Jan 27, 2016 4:17 am 
Beginner
Beginner

Joined: Mon Jul 27, 2015 4:03 am
Posts: 29
I tried your suggestions but I still continue to get the same exception. Anything else you find wrong here ?

Thanks,
Rajesh


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Wed Jan 27, 2016 6:18 am 
Beginner
Beginner

Joined: Mon Jul 27, 2015 4:03 am
Posts: 29
Ok, at last some hope. There were few mapping issues. However resolving those couldn't solve the exception. Finally what worked is disabling the optimistic locking . After removing the @Version annotation, everything worked like a charm.

So my follow up question is, will optimistic locking not work with OGM ?

Thanks,
Rajesh


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Wed Jan 27, 2016 6:54 am 
Hibernate Team
Hibernate Team

Joined: Fri Sep 09, 2011 3:18 am
Posts: 295
Optimisti Locking in MongoDB with OGM: https://docs.jboss.org/hibernate/ogm/5. ... ticlocking


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Fri Jan 29, 2016 7:38 am 
Beginner
Beginner

Joined: Mon Jul 27, 2015 4:03 am
Posts: 29
Thanks Davide. Yes I did look at the documentation before. However I am unable to understand where two different sessions are coming in picture. All udpates are happening within the same transaction boundary as demarcated by Spring. What can I do to debug this further ? Any particular logging option which might help in identifying the underlying separate transactions ?

Thanks,
Rajesh


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Mon Feb 01, 2016 5:17 am 
Hibernate Team
Hibernate Team

Joined: Fri Sep 09, 2011 3:18 am
Posts: 295
Could you show us the fixed mapping?


Top
 Profile  
 
 Post subject: Re: Bidirectional *-1 association causing StaleObjectStateExcept
PostPosted: Mon Feb 01, 2016 5:34 am 
Hibernate Team
Hibernate Team

Joined: Fri Sep 09, 2011 3:18 am
Posts: 295
In EmployeeService:
Quote:
entity.setCurrentDept(null);


I'm not familiar with Spring; out of curiosity, why do you need this?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.