web-dev-qa-db-ja.com

HibernateとSpringを使用したオプティミスティックロックの実装

更新が失われる状況を回避するために、オプティミスティックロックを実装しようとしています。私のアプリケーションでは、2つのユーザーが同じレコードと最初のユーザーいくつかの変更とそれを更新を取得するとき。この変更は、同じレコードを表示する2番目のユーザーには表示されず、自分で変更を加えて更新します。そのため、最初の人の変更は失われます。これを防ぐために、私は次のように書いていますが、それでも問題は解決しません。私はこのコンセプトに新しいです、問題を特定することはできません。

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>
    
  • エンティティクラス

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

前もって感謝します

6
Leejoy

JPA/Hibernate Optmisticロックは、フィールドを使用して最後に変更されたバージョン(timestamp、longなど)を格納し、セッション内のエンティティのバージョンをデータベース内のエンティティと比較して、変更を保存できるかどうかを確認することで機能します。

これを機能させるには、エンティティに@Versionアノテーションが付けられたフィールドが必要です。

例については、以下を参照してください。

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

これをWebアプリケーションで機能させるには、さらに検討する必要があります。ただし、2人が同じエンティティをロードして編集し、しばらくしてから編集を送信すると、長時間実行されるセッションを使用していない限り、エンティティは両方とも成功する可能性があります。編集中は、フォームの送信時にデータベースから再読み込みされ、入力されて保存されます。

例えばリビジョン1のエンティティ

  • ユーザー1が編集のためにロードされます:1でのリビジョン
  • ユーザー2が編集のためにロード:1でのリビジョン
  • ユーザー2がフォームを送信します:エンティティ(r1)がロードされ、フィールドがバインドされ、エンティティが保存されます:リビジョンは2です。
  • ユーザー1がフォームを送信します:エンティティ(r2)がロードされ、フィールドがバインドされ、エンティティが保存されます:リビジョンは3です。

したがって、これが機能するためには、ロードされた時点でのエンティティリビジョンを格納するフォームを使用して非表示フィールドを送信することを検討できます。したがって、上記の最後のステップで、ユーザー1が送信すると、リビジョンフィールドが1に戻され、DB内のレコードがr2であるため、更新は失敗します。

12
Alan Hay