使用Hibernate和Spring实现乐观锁

Lee*_*joy 6 spring hibernate optimistic-locking optimistic-concurrency

我正在尝试按顺序实现乐观锁定以避免丢失更新情况.在我的应用程序中,两个用户获取相同的记录,第一个用户通过一些更改更新它.查看相同记录的第二个用户看不到此更改,他自己进行了一些更改并对其进行了更新.由此导致第一批人员失去了.为了防止这种情况,我写了以下内容,但问题仍然存在.我是这个概念的新手,无法识别问题.

我试图通过阅读doc 11.3.4 来实现这一目标.自定义自动版本控制部分.

  • 配置文件

    <?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>
    
    Run Code Online (Sandbox Code Playgroud)
  • 实体类

    @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
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 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();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

提前致谢

Ala*_*Hay 12

JPA/Hibernate Optmistic锁定通过使用某个字段来存储上次修改的版本(例如,timestamp,long),然后将会话中的实体版本与数据库中的实体进行比较,以查看是否可以保存更改.

为此,您需要在实体中使用@Version注释一个字段.

请参阅下面的示例.

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

为了在Web应用程序中工作,需要进一步考虑,好像两个人加载相同的实体进行编辑,然后一段时间后提交他们的编辑,他们可能都会成功,除非你使用某种长时间运行的会话,实体正在编辑的将在表单提交,填充和保存的数据库中重新加载.

例如,修订版1中的实体

  • 用户1加载编辑:修订版1
  • 用户2加载编辑:修订版1
  • 用户2提交表单:实体(在r1)加载,字段绑定,实体保存:修订版为2.
  • 用户1提交表单:加载实体(在r2),绑定字段,保存实体:修订版本为3.

因此,为了实现这一点,您可以查看提交隐藏字段,其中包含在加载时存储实体修订的表单.因此,在上面的最后一步,当用户1提交时,修订字段将被设置回1并且更新将失败,因为DB中的记录为r2.

  • @Nathan隐藏字段版本号对我来说不是问题.您如何阻止用户只是刷新页面,忽略已完成的修改,将所有字段设置为它们的内容并再次提交?您不能阻止对数据具有写访问权限的人有意破坏数据. (4认同)
  • 我会避免使用隐藏字段,因为恶意用户可以更改这些值并使系统进入非预期状态.为了安全起见,应在服务器端维护状态.请参阅https://www.owasp.org/index.php/Web_Parameter_Tampering (3认同)