使用Hibernate和Spring实现乐观锁 [英] Implementing Optimistic lock using Hibernate and Spring

查看:145
本文介绍了使用Hibernate和Spring实现乐观锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图按顺序实现乐观锁定,以避免丢失更新情况。在我的应用程序中,当两个用户获取相同的记录时,第一个用户通过一些更改来更新它。对于查看同一记录的第二位用户而言,此更改不可见,并且他自己进行一些更改并更新它。由此导致的第一批人变更失败。为了防止这种情况发生,我写了以下内容,但问题仍然存在。我是这个概念的新手,无法确定问题。



我试图通过阅读 doc 11.3.4。定制自动版本控制部分。




  • 配置文件

     <?xml version =1.0encoding =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 =txManagerclass =org.springframework.jdbc.datasource.DataSourceTransactionManager>
    < property name =dataSourceref =dataSource/>
    < / bean>

    < bean id =hibernateTemplateclass =org.springframework.orm.hibernate3.HibernateTemplate>
    < property name =sessionFactoryref =sessionFactory/>
    < / bean>

    < bean id =sessionFactoryclass =org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean>
    < property name =dataSourceref =dataSource/>
    < property name =annotatedClasses>
    < list>
    <值> server.bo.Dept< /值>
    <值> server.bo.Emp< /值>
    < / list>
    < / property>
    < property name =hibernateProperties>
    <道具>
    < prop key =hibernate.dialect> org.hibernate.dialect.SQLServer2008Dialect< / prop>
    < prop key =hibernate.show_sql> false< / prop>
    < /道具>
    < / property>
    < / bean>
    < bean id =dataSourceclass =org.springframework.jdbc.datasource.DriverManagerDataSource>
    < property name =driverClassNamevalue =net.sourceforge.jtds.jdbc.Driver/>
    < property name =urlvalue =$ {db.url}/>
    < property name =usernamevalue =$ {db.username}/>
    < property name =passwordvalue =$ {db.password}/>
    < / bean>
    < bean id =deptDAOclass =server.dao.DeptDAOImpl>
    < property name =hibernateTemplateref =hibernateTemplate/>
    < / bean>
    < / beans>


  • 实体类

      @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;
    $ b $ 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';
    列表< Dept> deptDetailsList = getHibernateTemplate()。find(query,param);
    Dept deptDetails = null; (deptDetailsList!= null&& deptDetailsList .size()> 0)
    deptDetails =(Dept)deptDetailsList.get(0);
    返回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();


    code $

    $ b $ p

    提前致谢

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



    为此,您需要拥有

    请参阅下面的示例。



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



    要在Web应用程序中工作,需要进一步思考,但是如果两个人加载相同实体进行编辑,然后一段时间后提交他们的编辑他们可能会成功,除非哟您正在使用某种长时间运行的会话,正在编辑的实体将从表单提交,填充和保存的数据库中重新加载。

    例如。


    • 用户1加载编辑:修改1

    • 用户2加载编辑:修订于1

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

    • user 1 submits form:entity(at r2)is loaded,fields are bound,entity is saved:revision is 3.



    为了这个工作,你可以看看提交一个隐藏的字段,其中存储着实体修改的表单。因此,在上面的最后一步,当用户1提交时,修订字段将被设置回1,并且更新将失败,因为DB中的记录与r2一样。


    I am trying to implement Optimistic locking in-order to avoid lost update situation. In my application when two user fetch same record and the first user updates it with some changes. This change is not visible to the second user who view the same record and he makes some changes by himself and updates it. Due to which the first persons change is lost. In-order to prevent this I have written the following but still the issue persist. I am new to this concept, not able to identify the issue.

    I have tried to achieve this by reading doc 11.3.4. Customizing automatic versioning section.

    • The 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();
          }
      }
      

    Thanks in advance

    解决方案

    JPA/Hibernate Optmistic locking works by using some field to store the last modified version (e.g. timestamp, long) and then comparing the version of the entity in the session with the entity in the database to see if the change can be saved.

    For this to work you need to have a field in your entity annotated with @Version.

    See below for an example.

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

    For this to work in a web application requires further thought however as if two people load the same entity for editing and then some time later submit their edits they will likely both succeed as, unless you are using some kind of long running session, the entity being edited will be reloaded from the database on form submit, populated and saved.

    e.g. Entity at Revision 1

    • user 1 loads for edit: revision at 1
    • user 2 loads for edit: revision at 1
    • user 2 submits form: entity (at r1) loaded, fields are bound, entity is saved: revision is 2.
    • user 1 submits form: entity (at r2) is loaded, fields are bound, entity is saved: revision is 3.

    So for this to work you could look at submitting a hidden field with the form which stores the entity revision at the time it was loaded. So, on the last step above, when user 1 submits, the revision field will be set back to 1 and the update will fail because the record in the DB as at r2.

    这篇关于使用Hibernate和Spring实现乐观锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆