从ET2开发中领悟到的(续)

来源:互联网 发布:巨杉数据库 福利 编辑:程序博客网 时间:2024/05/17 12:49
 

1、  设计表时,如果属于同种类型的字符串保存,可以使用分隔符来分隔保存数据,可以避免存储多条记录或多个字段;操作上只需在存储之前加上分隔符,读取时分割一下就可以了。

2、  对于标志之类的字段,如果可以使用01表示,那么可以设计在一个字段之类,使用每一位来表示不同的含义,可以利用的函数BITAND(位与运算)来判断标志位. 在存储过程中需要创建一个函数BITOR(位或运算)来设置标志位

create or replace function BITOR(x in number, y in number) return number is
  Result number;
begin
  Result:=(x + y) - BITAND(x, y);
  return(Result);
end BITOR;

3、  对于数字或日期,可以使用trunc方法来截取指定长度的内容,该方法只截取,不做舍入。要做舍入使用round方法;日期天数加减直接使用+-号就可一了,月份加减使用add_months函数

4、  在实际使用中,经常会有带in的子查询,如where id in (1,2,3)这样的情况,但是如果很多这样的语句在数据库中出现,将引起数据库的大量硬解析与共享池SQL碎片。所以,在实际应用中,可以采用其他方法,将这些in list给绑定起来。
如果需要绑定in list,首先,需要创建两个类型(type)
针对数据类型的
CREATE OR REPLACE TYPE NUMTABLETYPE as table of number;
针对字符串类型的(每个list的单元大小不要超过1000字节)
create or replace type vartabletype as table of varchar2(1000);
然后创建两个相关的函数

数字列表函数

代码:

create or replace function str2numList( p_string in varchar2 ) return numTableType
as
v_str long default p_string ||
',';
v_n number;
v_data numTableType := numTableType();
begin
loop
v_n := to_number(instr( v_str,
',' ));
exit when (nvl(v_n,
0) = 0);
v_data.extend;
v_data( v_data.count ) := ltrim(rtrim(substr(v_str,
1,v_n-1)));
v_str := substr( v_str, v_n+
1 );
end loop;
return v_data;
end;

create or replace function str2varList( p_string in varchar2 ) return VarTableType
as
v_str long default p_string ||
',';
v_n number;
v_data VarTableType := VarTableType();
begin
loop
v_n :=instr( v_str,
',' );
exit when (nvl(v_n,
0) = 0);

v_data.extend;
v_data( v_data.count ) := ltrim(rtrim(substr(v_str,
1,v_n-1)));
v_str := substr( v_str, v_n+
1 );
end loop;
return v_data;
end;

创建之后,我们就可以采用如下的方式来使用in list的绑定了。如可以采用如下的三种方案

代码:

SELECT /*+ ordered use_nl(a,u) */ id, user_id, BITAND(promoted_type,4) busauth


 from table(STR2NUMLIST(:bind0)) a,


 bmw_users u


 where u.user_id = a.column_value


SELECT  /*+ leading(a) */ id, user_id, BITAND(promoted_type,4) busauth


 from bmw_users u where user_id in


 (select * from table(STR2NUMLIST(:bind0)) a);


SELECT  /*+ index(bmw_users UK_BMW_USERS_USERID) */ id, user_id


 from bmw_users where user_id in


 (SELECT * FROM THE (SELECT CAST(STR2NUMLIST(:bind0) AS NUMTABLETYPE) FROM dual) WHERE rownum<1000)



在如上的方案中,以上语句中的hint提示,是为了稳定执行计划,防止Oraclein list的错误估计而导致走hash连接。一般建议采用第一种方法,比较简单可靠并且可以指定稳定的计划。但是要求数据库的版本比较高,在老版本中(8i),可能只能采用第三种方法。总的来说,12两种方法比3要少6个逻辑读左右

5、  程序中关键地方一定要打印日志,对日志进行分级打印,异常日志统一在业务层最高层进行处理,其他底层异常包装后直接向业务层抛出。日志信息一定要有助于日后错误查找,不要打只打印无用的系统出错啦等,而其他有用的信息都没有。

6、  如果是相关的一组常量,则可以考虑设计枚举来进行操作,例如:订单状态,并且将状态相关的操作都封装在枚举中进行操作。颜色等也可以进行类似的封装:

 

/**

 * @author huanggang

 * @since 2007-11-13下午05:21:31

 *

 */

public enum Color {

    Red(1) {// 红色

       public Color nextColor() {

           return Blue;

       }

 

    },

    Blue(2) {// 蓝色

       public Color nextColor() {

           return Black;

       }

 

    },

    Black(3) {// 黑色

       public Color nextColor() {

           return Red;

       }

 

    };

    /**

     * 保存实际的值,这个类型可以是Object等其他类型

     */

    private int value;

 

    private Color(int value) {

       this.value = value;

    }

 

    /**

     * 得到当前值

     *

     * @return

     */

    public int getValue() {

       return this.value;

    }

 

    /**

     * 当前颜色的下一个颜色

     * @return

     */

    public abstract Color nextColor();

 

        /**

     * 是否是红色,其他颜色都可以这样定义

     * @return

     */

    public boolean isRed() {

       return isRed(this.value);

    }

   

    public boolean isRed(int value){

       return Red.value==value;

    }

}

 

7、  鉴于网络上对于ibatis的缓存介绍不够详细,特别是对于session的概念没有明确指出,都是人云亦云sqlMapClient相当于一个session,本人阅读了ibatis源代码,得出如下结论:

 

Ibatis一个sqlMapClient实例对应一个SqlMapExecutorDelegate实例,如果sqlMap被多线程执行,那么会共享这两个实例,ibatis对于每个线程都会创建一个SqlMapSessionImpl实例来表示session这个概念,每个线程的数据库操作方法都在本session内执行,SqlMapExecutorDelegate保存了一个ThreadLocal来保存最大同时能够运行的session数目,也就是配置文件中的maxSessions="800"。一个SqlMapSessionImpl对应ThreadLocal中的一个session.当执行查询操作时,如果有缓存,ibatis会首先根据参数、statementid、缓存的idhash一个CacheKey出来,如果该缓存是是可读写缓存,那么将本sessionhash进去,然后以这个对象为key查找缓存,由于有了session的特征,这样对于每一个session中查询出的数据的修改都不会影响另一个session的数据。如果该缓存是只读缓存,则不将session的特征hash进去,意思就是说,该sqlmap实例中的所有session都可以共享这一个查询出来的数据,对数据的修改也会造成对另一个session读出数据的影响。

 所以,按照这个道理,cache的配置中如果readonlytrue,则该sqlMap的所有session共享该实例,如果修改其中一个实例,那么会对其他session造成影响。如果readonlyfalse,由于该实例只是对该session有效,所以,修改也只会对本session有效。Serialize参数为true并且readonlyfalse时,将会对缓存数据进行序列化保存,读取时再反序列化。如果多个session访问,则Serialize会返回不反序列化之后的对象(相当于复制的对象,也可以这样理解,处理这一点和readonlytrue时不一样,其他处理方式都一样);当readonlytrue时,不论是否设置了Serialize,都会忽略该参数的设置,处理的结果和值设置readonly效果一样。

下面是两个关于该属性设置的关键代码,一读就明白了。(红色部分)

cacheModel.java 关键部分

/**

   * Get an object out of the cache.

   * A side effect of this method is that is may clear the cache if it has not been

   * cleared in the flushInterval.

   *

   * @param key The key of the object to be returned

   * @return The cached object (or null)

   */

  public Object getObject(CacheKey key) {

    synchronized (this) {

      if (flushInterval != NO_FLUSH_INTERVAL

          && System.currentTimeMillis() - lastFlush > flushInterval) {

        flush();

      }

    }

 

    Object value = null;

    synchronized (getLock(key)) {

      value = controller.getObject(this, key);

    }

 

    if (serialize && !readOnly && (value != NULL_OBJECT && value != null)) {

      try {

        ByteArrayInputStream bis = new ByteArrayInputStream((byte[]) value);

        ObjectInputStream ois = new ObjectInputStream(bis);

        value = ois.readObject();

        ois.close();

      } catch (Exception e) {

        throw new NestedRuntimeException("Error caching serializable object.  Be sure you're not attempting to use " +

            "a serialized cache for an object that may be taking advantage of lazy loading.  Cause: " + e, e);

      }

    }

 

    synchronized (STATS_LOCK) {

      requests++;

      if (value != null) {

        hits++;

      }

    }

 

    return value == NULL_OBJECT ? null : value;

 

  }

 

  /**

   * Add an object to the cache

   *

   * @param key   The key of the object to be cached

   * @param value The object to be cached

   */

  public void putObject(CacheKey key, Object value) {

    if (null == value) value = NULL_OBJECT;

    if (serialize && !readOnly && value != NULL_OBJECT) {

      try {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        ObjectOutputStream oos = new ObjectOutputStream(bos);

        oos.writeObject(value);

        oos.flush();

        oos.close();

        value = bos.toByteArray();

      } catch (IOException e) {

        throw new NestedRuntimeException("Error caching serializable object.  Cause: " + e, e);

      }

    }

    synchronized (getLock(key)) {

      controller.putObject(this, key, value);

    }

  }

 

CachingStatement.java 关键部分

  public CacheKey getCacheKey(RequestScope request, Object parameterObject) {

    CacheKey key = statement.getCacheKey(request, parameterObject);

    if (!cacheModel.isReadOnly() && !cacheModel.isSerialize()) {

      key.update(request.getSession());

    }

    return key;

  }