Optimistic locking implementation using Hibernate and Spring

I am trying to implement optimistic locking to avoid losing the update situation. In my application, when two users select the same record and the first user updates it with some changes. This change is not displayed to the second user who is viewing the same record, and he makes some changes and updates it. Because of what the first faces are lost. To avoid this, I wrote the following, but the problem still persists. I am new to this concept, unable to identify the problem.

I tried to achieve this by reading doc 11.3.4. Setting up the automatic version.

  • configuration file

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <tx:annotation-driven transaction-manager="txManager"/> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="annotatedClasses"> <list> <value>server.bo.Dept</value> <value>server.bo.Emp</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.SQLServer2008Dialect</prop> <prop key="hibernate.show_sql">false</prop> </props> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </bean> <bean id="deptDAO" class="server.dao.DeptDAOImpl"> <property name="hibernateTemplate" ref="hibernateTemplate"/> </bean> </beans> 
  • Entity class

     @Entity @Table(name = "Dept") @org.hibernate.annotations.Entity(dynamicUpdate = true,optimisticLock = OptimisticLockType.ALL) public class Dept{ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") Long id; @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "deptId") @Fetch(FetchMode.SELECT) @OrderBy(value = "id DESC") List<Emp> Emplist; public Dept() {} // Getters and setters } 
  • DAO Impl

     public class DeptDAOImpl extends HibernateDaoSupport implements DeptDAO { @Transactional(readOnly = true, propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) public Dept getDeptById(Long id) { Object param[] = new Object[]{id}; String query = "select d from Dept d where d.id=? and d.deleted='0'"; List<Dept> deptDetailsList = getHibernateTemplate().find(query,param); Dept deptDetails = null; if(deptDetailsList !=null && deptDetailsList .size()>0) deptDetails = (Dept)deptDetailsList.get(0); return deptDetails ; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) public long updateDept(Dept dept) { if (dept.getId() == null) { getSession().save(dept); } else { getSession().update(dept); } if (dept.getEmplist() != null) { final int size = dept.getEmplist().size(); for (int i = size - 1; i >= 0; i--) { Emp emp = dept.getEmplist().get(i); if (emp.getDeptId() == null) { emp.setDeptId(dept.getId()); } if (RecordStatus.NEW.equals(emp.getRecordStatus())) { getSession().save(emp); } else if (RecordStatus.DELETED.equals(emp.getRecordStatus())) { getSession().delete(emp); } else if (RecordStatus.MODIFIED.equals(emp.getRecordStatus())) { getSession().update(emp); } } } return dept.getId(); } } 

Thank you in advance

+6
source share
1 answer

JPA / Hibernate Optimistic locking works by using some field to store the last modified version (for example, timestamp, long), and then compares the version of the object in the session with the entity in the database to see if the change can be saved.

To do this, you need to have a field in the object annotated with @Version.

The following is an example.

http://www.javacodegeeks.com/2012/11/jpahibernate-version-based-optimistic-concurrency-control.html

To do this, working in a web application requires further thought, as if two people uploaded the same object for editing, and then after a while sent their changes, they are likely to be successful if you do not use some kind of long session, the edited object will be reloaded from the database in the submit form, filled and saved.

eg. Object in edition 1

  • user 1 download for editing: version 1
  • user 2 downloads for editing: revision 1
  • User 2 submits the form: the object (in r1) is loaded, the fields are attached, the entity is saved: version is 2.
  • User 1 submits the form: the object (in r2) is loaded, the fields are attached, the object is saved: version 3.

So, for this you can see how to send a hidden field with the form in which the revision of the object is stored at the time of its loading. Thus, in the last step above, when user 1 sends, the revision field will be set to 1, and the update will fail because the database entry is like r2.

+12
source

Source: https://habr.com/ru/post/956261/


All Articles