欢迎进入Wiki » FAQ » 如何解决Domain类中的多个Oracle大字段的读写问题?

如何解决Domain类中的多个Oracle大字段的读写问题?

在2013-11-26 12:01上被李小翔修改
评论 (0) · 附件 (0) · 记录 · 信息

供稿人:刘路峰

Domain类中定义多个大字段(CLOB)、并且使用Oracle数据库时,读写时容易发生 “ORA-22295: 不能把超过 4000 字节数据绑定到语句 1 中的 LOB 和 LONG”、“java.sql.SQLException: 流已被关闭” 等异常,详见《采用Oracle数据库、并且Domain类中定义多个大字段时的隐患》。

在BroFramework中,可以按照下面的代码样例编写大字段相关代码,以规避上述问题。

定义Domain类:

import bropen.toolkit.orm.OracleClobType;

/**
 * 一个包含多个CLOB字段的Domain类
 */

public class ProcessDefinition {
   /** 第一个大字段 */
    String foo;
   
   /** 第二个大字段 */
    String bar;
   // 从第二个开始的所有大字段,需要创建一个private的相似名称字段,作为临时字段备用
   private String bar2;
   
   static constraints = {
        foo(nullable:true, maxSize:999999999)
        bar(nullable:true, maxSize:999999999)
   }
   static mapping = {
       // 所有大字段类型改为“OracleClobType.getType()”,如果多数据源,可以getType()中传入数据源的名称
       foo type: OracleClobType.getType()
        bar type: OracleClobType.getType()
   }
   
   // 第二个大字段开始的所有大字段,为了能在代码中正确取到大字段,需要创建此 get 方法
   String getBar2() {
       return this.bar;    // 注意返回值,是真正的数据呦
   }
   
   // 第二个大字段开始的所有大字段,必须创建一个返回null的get方法,以欺骗hibernate
   String getBar() {
       return null;        //返回值必须是null
   }
   
   // beforeInsert 和 beforeUpdate 事件中:
   // 第二个大字段开始的所有大字段,将大字段的值赋给临时字段
   // 注:只能在这个事件中赋值,因为 beforeValidate 中赋值时会不成功
   def beforeInsert () {
        bar2 = bar;
   }
   def beforeUpdate () {
         bar2 = bar;
   }
   
   // afterInsert 和 afterUpdate 事件中:
   // 第二个大字段开始的所有大字段,单独更新每个大字段
   def afterInsert() {
        updateBar();
   }
   def afterUpdate() {
        updateBar();
   }
   
   // 更新大字段
   private void updateBar() {
        ProcessDefinition.executeUpdate(
           "update bropen.bpm.definition.ProcessDefinition p set p.bar=? where p.id=?",
           [this.bar2, this.id]
       );
       // 必须赋值回去,因为insert时系统会把bar字段变成null
       this.bar = this.bar2;
   }
}

所有对此 domain 实体的保存,如果存在多个 save,则只有对这个实体的最后一次 save 调用时才可以使用 flush:true。例如:

// 错误的代码
definition.setBar( "s1234" );
def su = definition.save(flush:true);
definition.setBar( "s4567" );
def su = definition.save(flush:true);

//正确的代码:
definition.setBar( "s1234" );
def su = definition.save();
definition.setBar( "s4567" );
def su = definition.save(flush:true);

所有对此 domain 实体的查询,不能直接 get 大字段,只能 get 临时字段。例如

// 错误的代码:总是返回null
println definition.bar;
println definition.getBar();
// 正确的代码
println definition.bar2;
println definition.getBar2();
在2013-11-26 12:00上被李小翔创建

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号