尝试使用Hibernate在Grails中使用乐观锁定

时间:2015-03-18 12:16:32

标签: hibernate grails jpa

我在使用hibernate域类的小型Grails应用程序中发现乐观锁定对我不起作用。域类很简单:

@Entity
@Table(name = "DELME", uniqueConstraints = @UniqueConstraint(columnNames = "ID"))

public class DelmeV implements java.io.Serializable {
    private int id;
    private Long version;
    private String d2;
    private String dsc;

    // ... constructors goes here
    @Id
    @Column(name = "ID", nullable = false)
    public int getId() {...}
    public void setId(int id) {...}

    @Version
    @Column(name = "VERSION")
    public Long getVersion() { ... }
    public void setVersion(Long version) { ... }

    @Column(name = "D2")
    // appropriate getters and setters goes here ...

    @Column(name = "DSC") 
    // appropriate getters and setters goes here ...

}

控制器代码是脚手架:

@Transactional(readOnly = true)
class DelmeVController {

// methods which are out of interest skipped... 
@Transactional
def update(DelmeV delmeVInstance) {
    if (delmeVInstance == null) {
        notFound()
        return
    }

    if (delmeVInstance.hasErrors()) {
        respond delmeVInstance.errors, view:'edit'
        return
    }

    delmeVInstance.save flush:true

    request.withFormat {
        form multipartForm {
            flash.message = message(code: 'default.updated.message', args: [message(code: 'DelmeV.label', default: 'DelmeV'), delmeVInstance.id])
            redirect controller:"delmeV", id:delmeVInstance.id
        }
        '*'{ respond delmeVInstance, [status: OK] }
    }
}
}

测试程序如下:

  1. 运行grails应用程序,将带有给定id(例如1)的Delme行带入编辑表单。
  2. 执行“sql update delme set d2 ='bad value',version = version + 1 where id = 1;”在数据库服务器上。
  3. 执行“sql select * from delme where id = 1;”在数据库服务器上并验证d2是否在步骤2中设置了值,并且版本实际上已递增。
  4. 在打开的编辑表单中更改DSC字段值,然后点击“更新”按钮。
  5. 结果:更新顺利进行,步骤2中设置的D2值无处可去,没有异常发生。坏。
  6. 所以,问题1 :上面有什么问题?为什么乐观锁定不适用于我?

    好吧,我放弃了默认的乐观锁定行为并写了我自己的(考虑到我们的实际业务表没有'版本'列,悲观锁不是OLTP env中的一个选项): 我将'version'设为瞬态,将其填入onLoad方法并检入preUpdate:

    @Entity
    @Table(...) 
    
    public class DelmeV  implements org.hibernate.classic.Lifecycle {
      // unisteresting stuff...
    
      @Transient
      private String version;
      // getters, setters ...
    
      public void onLoad (Session s, java.io.Serializable id) {
        if (this != null) {
          try {
            com.fasterxml.jackson.databind.ObjectMapper om = new com.fasterxml.jackson.databind.ObjectMapper();
            om.configure (com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            this.version = om.writeValueAsString (this);
          } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
             System.out.println ("Jackson exception caught on #" + this.id);
             e.printStackTrace();
             this.version = "/x/";
         }
       }
    
       @javax.persistence.PreUpdate
       public void actualOnUpdate (Session s) throws StaleObjectStateException {
    
         SessionFactory sessionFactory = s.getSessionFactory();
         Session ns = null;
         Object newInstance = null;
         String dbVersion = null;
    
         try  {
           ns = sessionFactory.openSession();
           Transaction tx = ns.beginTransaction ();
           newInstance =   ns.get (this.getClass(), this.id);
           dbVersion = ((DelmeV) newInstance).getVersion();
           tx.commit();
         } catch (Throwable ex) {
           System.out.println ("Session creation exception caught on " + this.getClass().getName() + ", id=" + this.id);
           ex.printStackTrace();
         } finally {
           ns.close();
         }
       boolean eq = dbVersion.equals (this.version);
       if (!eq) {
         throw new StaleObjectStateException (this.getClass().getName(), this.id);
       }
     }
    }
    

    我注册了用于扫描@preUpdate方法的域类的事件侦听器,只要Grails没有自动调用它就调用它。到目前为止这么好,解决方案正在运行,但它恰好与其他框架(Spring Batch,特别是)不兼容,因为JPA要求@preUpdate方法使用空参数列表,我不知道如何找出当前Session或SessionFactory(或者EntityManagerFactory,只要涉及JPA,来自preUpdate方法(在Grails中我将Session作为参数从事件监听器调用preUpdate方法)。那么问题2 :如何以一致的,独立于框架的方式从域方法中找出Session / SessionFactory / EntityManagerFactory?

0 个答案:

没有答案