2005年12月05日

最近碰到了一个由于java方法的二进制签名导致的问题,促使我去研究了一下java方法的二进制签名,下面就来和大家一起分享。

我在程序中使用了jdom.jar,调用大概方式如下:
    Element a = new Element();
    Element b = new Element();
    a.addContent(b);

程序的编译环境用的是jdom的beta9版本,而运行环境用的是beta10版本。结果就报以下错误:
    java.lang.NoSuchMethodError: org.jdom.Element.addContent(Lorg/jdom/Element;)Lorg/jdom/Element;
    
后来查看原因,发现在beta9中,addContent方法的定义如下:
    Element.addContent(Element x)

而在beta10中,Element改成了Content的子类,并把原来的addContent改为:
    Element.addContent(Content x)

虽然beta10是向下兼容beta9的(即使用beta9的程序可以平滑的迁移到beta10), 但确无法实现代码的二进制兼容,即基于beta9编译的程序能在beta10的环境下使用,或者反之。

究其原因,是因为java是动态绑定的(c++中的分静态绑定和动态绑定),它在运行时才根据方法的二进制签名查找最终调用的方法。在上面的例子中,基于beta9编辑的类中,其调用的方法签名就是(Lorg/jdom/Element;)Lorg/jdom/Element, 而beta10环境下并没有符合该签名的addContent方法,自然就导致的java.lang.NoSuchMethodError。


知道了原因,下面我们就来了解一下java方法的二进制签名。java方法的签名有两部分组成,就是我们熟知的形参和返回值。但在报错或者其他输出情况下,其显示方式比较特别。比如下面的方法:
    Element Element.addContent(Element x)

其二进制签名显示成:
    (Lorg/jdom/Element;)Lorg/jdom/Element

这里,对象被表示成L开始,;结尾的标识,中间是类名。对于基础类型,则按如下显示:
        Type     Chararacter
        boolean      Z
        byte             B
        char            C
        double       D
        float            F
        int               I
        long           J
        object        L
        short         S
        void           V
        array          [
        
        


    

2005年07月11日

同时访问一个或多个表的多个记录时就要用到连接查询。连接查询的语意是要从From子句里的各个表的笛卡儿乘积里面找出符合查询条件的所有记录。

在做连接查询时,有时候会若干次重复计算同一目标列表;这种情况在布尔表达式包含"or"时尤甚,为了避免重复,你应该使用 select distinct 语句。

比如,有如下三个表:
SQL> select * from t_company_organ;

PARENT_ID ORGAN_ID
——— ——–
          1
1         2
1         3
1         4
1         5
2         2-1
2         2-2
2         2-3
2         2-4
2         2-5

10 rows selected

SQL> select * from t_area_map;

ORGAN_ID AREA_UD
——– ——-
2        
1        
3


SQL> select * from t_voucher_data;

ACCOUNT_ID ORGAN_ID     AMOUNT
———- ——– ———-
9999       2            100.00


用如下语句查询某区域内各个机构的业务量就会发生重复计算:
SQL> select  a.account_id,a.organ_id,a.AMOUNT from t_voucher_data a,t_area_map b
  2  where ( a.organ_id IN ( SELECT organ_id FROM t_company_organ START WITH parent_id=b.organ_id CONNECT BY parent_id=PRIOR organ_id)
  3   OR  a.organ_id = b.organ_id  );

ACCOUNT_ID ORGAN_ID     AMOUNT
———- ——– ———-
9999       2            100.00
9999       2            100.00


为了避免重复计算,就需要注意使用distinct。在复杂的sql语句中,是很容易犯这类错误的,而且很难察觉。

2005年05月29日

== 通用性 vs 客户化 ==
a. 通用性往往意味着低效、浪费;客户化在带来高效和快速发布的同时也常常导致不可重用;
b. 在做方案时,在通用性和客户化之间做合适的抉择是至关重要的,务必重视它;


== 技术 vs 业务 ==
a. 技术和业务就像男人和女人,很难有相同的观点。开发人员从技术角度总是希望建立一个理想的模型,而业务人员在提出需求时,是从来不会考虑模型的;
b.  技术人员切忌不要苛求为系统建立一个理想的模型,那常常是一条没有尽头的不归路。


== 保持良好的开发态度 ==
a. coding永远只是项目中的一小部分(大约30%)。开发人员千万不可眼里只有coding,有时业务知识,分析需求的能力更重要;
b.  一定要认识到,开发过程中遇到障碍是不可避免的,如果没有那是你的运气。当障碍使得项目不能按照你的设想平滑的前进时,千万不可心浮气澡。要知道,这些障碍通常就是项目的关键点所在,克服它们并不是浪费时间,而恰恰是在推动项目从一个关键点到达下一个关键点。一旦意识到这一点,你就能保持良好的心态。

2005年05月27日

【Db_name:】
对一个数据库(Oracle database)的唯一标识,该数据库为第一章讲到的Oracle database。这种表示对于单个数据库是足够的,但是随着由多个数据库构成的分布式数据库的普及,这种命令数据库的方法给数据库的管理造成一定的负担,因为各个数据库的名字可能一样,造成管理上的混乱。为了解决这种情况,引入了Db_domain参数,这样在数据库的标识是由Db_name和 Db_domain两个参数共同决定的,避免了因为数据库重名而造成管理上的混乱。这类似于互连网上的机器名的管理。我们将Db_name和 Db_domain两个参数用’.’连接起来,表示一个数据库,并将该数据库的名称称为Global_name,即它扩展了Db_name。 Db_name参数只能由字母、数字、’_’、’#’、’$’组成,而且最多8个字符。


 
【Db_domain:】
定义一个数据库所在的域,该域的命名同互联网的’域’没有任何关系,只是数据库管理员为了更好的管理分布式数据库而根据实际情况决定的。当然为了管理方便,可以将其等于互联网的域。

 
【Global_name:】
对一个数据库(Oracle database)的唯一标识,oracle建议用此种方法命令数据库。该值是在创建数据库是决定的,缺省值为Db_name. Db_domain。在以后对参数文件中Db_name与Db_domain参数的任何修改不影响Global_name的值,如果要修改 Global_name,只能用ALTER DATABASE RENAME GLOBAL_NAME TO <db_name.db_domain>命令进行修改,然后修改相应参数。
 
【Service_name:】
该参数是oracle8i新引进的。在8i以前,我们用SID来表示标识数据库的一个实例,但是在Oracle的并行环境中,一个数据库对应多个实例,这样就需要多个网络服务名,设置繁琐。为了方便并行环境中的设置,引进了Service_name参数,该参数对应一个数据库,而不是一个实例,而且该参数有许多其它的好处。该参数的缺省值为Db_name. Db_domain,即等于Global_name。一个数据库可以对应多个Service_name,以便实现更灵活的配置。该参数与SID没有直接关系,即不必Service name 必须与SID一样。

在tnsnames.ora中,既可以用service_name, 也可以用sid
 


【Net service name:】
网络服务名,又可以称为数据库别名(database alias)。是客户端程序访问数据库时所需要,屏蔽了客户端如何连接到服务器端的细节,实现了数据库的位置透明的特性。网络服务名被记录在tnsnames.ora文件中。




===关于错误ORA-12154:====
有时即使在tnsnames.ora文件中有相应的网络服务名,可是用该网络服务名连接时还会出错,出现这种情况的典型配置如下(在客户端的机器上):
tnsnames.ora中有:

HZUAT =
   (DESCRIPTION =
     (ADDRESS_LIST =
       (ADDRESS = (PROTOCOL = TCP)(HOST = 10.18.1.102)(PORT = 1521))
        )
        (CONNECT_DATA =
       (SERVICE_NAME = hzuat)
         )
      )

sqlnet.ora中有:
      
NAMES.DEFAULT_DOMAIN = staff.ebao.com

可当用 user/passwd@HZUAT去连时, 却报ora-12154错。 其原因就是因为sqlplus程序会自动到sqlnet.ora文件中找NAMES.DEFAULT_DOMAIN参数,假如该参数存在,则将该参数中的值取出,加到网络服务名的后面。解决办法一是删掉NAMES.DEFAULT_DOMAIN, 二是把tnsnames.ora改成
HZUAT.COM =
   (DESCRIPTION =
     (ADDRESS_LIST =
       (ADDRESS = (PROTOCOL = TCP)(HOST = 10.18.1.102)(PORT = 1521))
        )
        (CONNECT_DATA =
       (SERVICE_NAME = hzuat)
         )
      )


2005年04月21日

                                                                        —– 摘自 http://fengyu.china.com/dict.htm
            
  ORACLE的数据字典是数据库的重要组成部分之一,它随着数据库的产生而产生, 随着数据库的变化而变化,
体现为sys用户下的一些表和视图。数据字典名称是大写的英文字符。

    数据字典里存有用户信息、用户的权限信息、所有数据对象信息、表的约束条件、统计分析数据库的视图等。
我们不能手工修改数据字典里的信息。

  很多时候,一般的ORACLE用户不知道如何有效地利用它。

  dictionary   全部数据字典表的名称和解释,它有一个同义词dict
    dict_column   全部数据字典表里字段名称和解释
   
    如果我们想查询跟索引有关的数据字典时,可以用下面这条SQL语句:
    
    SQL>select * from dictionary where instr(comments,’index’)>0;
    
    如果我们想知道user_indexes表各字段名称的详细含义,可以用下面这条SQL语句:
    
    SQL>select column_name,comments from dict_columns where table_name=’USER_INDEXES’;
    
    依此类推,就可以轻松知道数据字典的详细名称和解释,不用查看ORACLE的其它文档资料了。
    
    下面按类别列出一些ORACLE用户常用数据字典的查询使用方法。
    
    一、用户

        查看当前用户的缺省表空间
        SQL>select username,default_tablespace from user_users;

    查看当前用户的角色
    SQL>select * from user_role_privs;
    
    查看当前用户的系统权限和表级权限
    SQL>select * from user_sys_privs;
    SQL>select * from user_tab_privs;

    二、表
        
        查看用户下所有的表
        SQL>select * from user_tables;
            
        查看名称包含log字符的表
        SQL>select object_name,object_id from user_objects
            where instr(object_name,’LOG’)>0;
        
        查看某表的创建时间
        SQL>select object_name,created from user_objects where object_name=upper(‘&table_name’);
        
        查看某表的大小
        SQL>select sum(bytes)/(1024*1024) as "size(M)" from user_segments
            where segment_name=upper(‘&table_name’);
        
        查看放在ORACLE的内存区里的表        
        SQL>select table_name,cache from user_tables where instr(cache,’Y')>0;
        
    三、索引
    
        查看索引个数和类别
        SQL>select index_name,index_type,table_name from user_indexes order by table_name;
        
        查看索引被索引的字段
        SQL>select * from user_ind_columns where index_name=upper(‘&index_name’);
        
        查看索引的大小
        SQL>select sum(bytes)/(1024*1024) as "size(M)" from user_segments
            where segment_name=upper(‘&index_name’);
        
    四、序列号
    
        查看序列号,last_number是当前值
        SQL>select * from user_sequences;
    
    五、视图
    
        查看视图的名称
        SQL>select view_name from user_views;
        
        查看创建视图的select语句
        SQL>select view_name,text_length from user_views;
        SQL>set long 2000;        说明:可以根据视图的text_length值设定set long 的大小
        SQL>select text from user_views where view_name=upper(‘&view_name’);
    
    六、同义词
    
        查看同义词的名称
        SQL>select * from user_synonyms;
    
    七、约束条件
    
        查看某表的约束条件
        SQL>select constraint_name, constraint_type,search_condition, r_constraint_name
            from user_constraints where table_name = upper(‘&table_name’);
            
    SQL>select c.constraint_name,c.constraint_type,cc.column_name  
        from user_constraints c,user_cons_columns cc
        where c.owner = upper(‘&table_owner’) and c.table_name = upper(‘&table_name’)
        and c.owner = cc.owner and c.constraint_name = cc.constraint_name
        order by cc.position;            
    
    八、存储函数和过程
    
        查看函数和过程的状态
        SQL>select object_name,status from user_objects where object_type=’FUNCTION’;
        SQL>select object_name,status from user_objects where object_type=’PROCEDURE’;
        
        查看函数和过程的源代码
        SQL>select text from all_source where owner=user and name=upper(‘&plsql_name’);
        
    九、触发器
    
        查看触发器

        set long 50000;
        set heading off;
        set pagesize 2000;

        select
        ’create or replace trigger "’ ||
                 trigger_name || ‘"’ || chr(10)||
         decode( substr( trigger_type, 1, 1 ),
                 ’A', ‘AFTER’, ‘B’, ‘BEFORE’, ‘I’, ‘INSTEAD OF’ ) ||
                      chr(10) ||
         triggering_event || chr(10) ||
         ’ON "’ || table_owner || ‘"."’ ||
               table_name || ‘"’ || chr(10) ||
         decode( instr( trigger_type, ‘EACH ROW’ ), 0, null,
                    ‘FOR EACH ROW’ ) || chr(10) ,
         trigger_body
        from user_triggers;
        

2005年03月25日

因为要同步优化oracle text index,因此就写一个同步优化的job, 如下:

declare
  job number;
begin
  dbms_job.submit(job=>job,
                    what=>’ctx_ddl.sync_index(‘’s_Index”);’,
                    next_date=>sysdate,
                    interval=>’SYSDATE+1/48′);
  commit;
  dbms_output.put_line(‘job ‘||job||’ has been submitted.’);
  dbms_job.submit(job=>job,
                    what=>’ctx_ddl.optimize_index(‘’s_Index”, ”FULL”);’,
                    next_date=>sysdate,
                    interval=>’SYSDATE+1/12′);
  commit;
  dbms_output.put_line(‘job ‘||job||’ has been submitted.’);
end;
/

可发现run时老是出现一下错误:

ERROR at line 1:
ORA-12011: execution of 1 jobs failed
ORA-06512: at “SYS.DBMS_IJOB”, line 406
ORA-06512: at “SYS.DBMS_JOB”, line 272
ORA-06512: at line 1

问题是这些ctx_ddl.sync_index 及 ‘ctx_ddl.optimize_index 在sqlplus里运行都没问题。于是拼命网上网下查资料,发现oracle的资料里面有这么一段话:

To use this script, you must be the index owner and you must have executeprivileges on the CTX_DDL package. You must also set the job_queue_processes parameter in your Oracle initialization file.

于是怀疑是要给job的owner显式的赋权限,即:

sql> grant execute on  ctx_ddl to <owner>;

可结果大失所望,还是同样的错误。

两天后,终于在网上看到这样一条信息:

this is a bug..infact it was happening in 8i and oracle said they will try to fix in 9i. u have to call the procedure in the ur job and embed the sync index in ur procedure. here is a snippet.u can try this way.

于是赶紧试验,修改如下:

–sync
create or replace procedure synchronize_index
is
begin
   ctx_ddl.sync_index(’s_Index’);
end synchronize_index;
/

–optimize
create or replace procedure optimize_index
is
begin
   ctx_ddl.optimize_index(’s_Index’, ‘FULL’);
end optimize_index;
/

declare
  job number;
begin
  dbms_job.submit(job=>job,
                    what=>’synchronize_index;’,
                    next_date=>sysdate,
                    interval=>’SYSDATE+1/48′);
  commit;
  dbms_output.put_line(‘job ‘||job||’ has been submitted.’);
  dbms_job.submit(job=>job,
                    what=>’optimize_index;’,
                    next_date=>sysdate,
                    interval=>’SYSDATE+1/12′);
  commit;
  dbms_output.put_line(‘job ‘||job||’ has been submitted.’);
end;
/

结果果然如他说的一样。

天哪,老毛说不要搞个人崇拜,看来我们也不能太相信oracle,没想到还有这种bug!!!!



2005年02月23日

问题描述:
在通常的三层构架下,客户通过Browser请求Web服务器查询数据库,而查询结果是上千条甚至是上百万条记录,要求查询结果传送到客户端浏览器并分页显示。


考虑因素:
1. Web服务器的资源消耗,包括:内存(用来存储查询结果),数据库相关资源(数据库连接对象,ResultSet对象等等);
2. DB服务器资源的消耗,包括游标,会话等等。
3. 网络开销,包括与数据库建立会话,传输查询结果等等。


JDBC中的几个重要Class:

A ResultSet object maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The next method moves the cursor to the next row, and because it returns false  when there are no more rows in the ResultSet object, it can be used in a while loop to iterate through the result set.

ResultSet是直接在数据库上建立游标,然后通过ResultSet的行位置定位接口来获得指定行位置的记录。当用户通过get方法获取具体纪录的内容时,ResultSet才从数据库把所需数据读到客户端。

Oracle的ResultSet实现似乎会在本地缓存用户读取过的数据,导致内存消耗会随读取数据的增加而增加,这样,如果一次查询并读取海量数据,即使读出数据后马上丢弃(比如直接写入文件),内存消耗也会随查询结果的增加而递增???????


The RowSet interface extends the standard java.sql.ResultSet interface.  A RowSet object may make a connection with a data source and maintain that connection throughout its life cycle, in which case it is called a connected rowset. A rowset may also make a connection with a data source, get data from it, and then close the connection. Such a rowset is called a disconnected rowset. A disconnected rowset may make changes to its data while it is disconnected and then send the changes back to the original source of the data, but it must reestablish a connection to do so.

RowSet是JDBC2.0中提供的接口,Oracle对该接口有相应实现,其中很有用的是 oracle.jdbc.rowset.OracleCachedRowSet。 OracleCachedRowSet实现了ResultSet中的所有方法,但与ResultSet不同的是,OracleCachedRowSet中的数据在Connection关闭后仍然有效。



解决方案一:直接使用ResultSet来处理

从ResultSet中将查询结果读入collection,缓存在HttpSession或有状态bean中,翻页的时候从缓存中取出一页数据显示。这种方法有两个主要的缺点:一是用户可能看到的是过期数据;二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。

对上述方法的一种改进是当用户第一请求数据查询时,就执行SQL语句查询,获得的ResultSet对象及其要使用的连接对象都保存到其对应的会话对象中。以后的分页查询都通过第一次执行SQL获得的ResultSet对象定位取得指定页的记录(使用rs.last();rs.getRow()获得总计录条数,使用rs.absolute()定位到本页起始记录)。最后在用户不再进行分页查询时或会话关闭时,释放数据库连接和ResultSet对象等数据库访问资源。每次翻页都只从ResultSet中取出一页数据。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是回缓存所有记录而占用大量内存,同时速度也非常慢。

在用例分页查询的整个会话期间,一个用户的分页查询就要占用一个数据库连接对象和结果集的游标,这种方式对数据库的访问资源占用比较大,并且其利用率不是很高。

优点:减少了数据库连接对象的多次分配获取,减少了对数据库的SQL查询执行。

缺点:占用数据库访问资源-数据库连接对象,并占用了数据库上的资源-游标;会消耗大量内存;



解决方案二:定位行集SQL查询

使用数据库产品的提供的对查询的结果集可定位行范围的SQL接口技术。在用户的分页面查询请求中,每次可取得查询请求的行范围的参数,然后使用这些参数生产取得指定行范围的的SQL查询语句,然后每次请求获得一个数据库连接对象并执行SQL查询,把查询的结果返回给用户,最后释放说有的数据库访问资源。

这种方式需要每次请求时都要执行数据库的SQL查询语句;对数据库的访问资源是使用完就立即释放,不白白占用数据库访问资源。 对特定(提供了对查询结果集可定位功能的)的数据库产品,如:Oracle(rowid或rownum ),DB2(rowid或rownum ()), PostgreSQL(LIMIT 和 OFFSET),mySQL(Limit)等。(MS SQL Server 没有提供此技术。)

下面是在oracle下的查询语句示例:
SELECT * FROM ( SELECT row_.*, rownum rownum_ FROM (…… ) row_ WHERE rownum <= {pageNumber*rowsPerPage}) WHERE rownum_ > {(pageNumber-1)*rowsPerPage}

优点:对数据库的访问资源(数据库连接对象,数据库游标等)没有浪费,这些资源的充分重复的利用。

缺点:对每次分页面查询请求要频繁的从Web容器中获得数据库访问资源(数据库连接对象和数据库游标)并建立连接;要依赖于具体的数据库产品的支持。



事实上方案一只适合中小数据量,海量数据恐怕一定要借鉴方案二。

本文只是抛砖引玉,希望能有更好的方案。


2005年01月21日



The previous article introduced the basic concept about Oracle Text. Now, let’s turn to the real world and show you an example. This example will let you search on multi-column of a table base on multi-language. In order to search on multi column, we  should use MULTI_COLUMN_DATASTORE, and in order to search multi language, we should use MULTI_LEXER which is a slot for special language lexer. Through this example, you will get a clear understanding about Oracle Text.

First, we will extend the simple in the previous article to have more column, and  insert some demo data into it:

DROP TABLE SEARCH_TEST CASCADE CONSTRAINTS ;

CREATE TABLE SEARCH_TEST (
  ID              VARCHAR2 (30)  NOT NULL,
  TITLE                  VARCHAR2 (80),
  LONG_DESCRIPTION       VARCHAR2 (1000),
  SHORT_DESCRIPTION      VARCHAR2 (100),
  KEY_WORDS              VARCHAR2 (425),
  LANG                VARCHAR2 (3),
  DUMMY_COLUMN1         VARCHAR2 (5),
  DUMMY_COLUMN2         VARCHAR2 (5),
  DUMMY_COLUMN3         VARCHAR2 (5),
  PRIMARY KEY ( ID )  );


insert into SEARCH_TEST (ID,TITLE,LONG_DESCRIPTION,SHORT_DESCRIPTION,KEY_WORDS,LANG) values(1,’tsunami hit the eaten asia’,'DEC. 26 2004, the tsunami hit the easten asia and cause great damage.’,'tsunami hit the eaten asia’,'tsunami’,'eng’);
insert into SEARCH_TEST (ID,TITLE,LONG_DESCRIPTION,SHORT_DESCRIPTION,KEY_WORDS,LANG) values(2,’海啸袭击东南亚’,'2004年十二月二十六号,海啸袭击东南亚,造成巨大破坏’,'海啸袭击东南亚’,'海啸’, ‘chi’);


Now, maybe you will confuse about the usage of column LANG, DUMMY_COLUMNX. Do not worry, when you reading on ,you will know.

Then, we will create some preference , which will be used to create the index.

In order to search on multicolumn, which also mean create index on multi column ,we should use MULTI_COLUMN_DATASTORE. In Oracle Text, the columns the index created on can be different with the content of index come from. The real content of Index is control by DATASTORE object. In our example, we will create a  MULTI_COLUMN_DATASTORE preference which will read the content from column TITLE, SHORT_DESCRIPTION, LONG_DESCRIPTION and KEY_WORDS. While creating index, we can create index on any column ,here we will create on a dummy column.

To search multi language content in these column, we should use MULTI_LEXER . MULTI_LEXER is a slot for special language lexer. You should create one language specific lexer for every language , then plug them into MULTI_LEXER. While indexing, the MULTI_LEXER will use the right lexer according to the lang in the language column , which should be specified while creating index.

Oracle Text use functions in package “CTX_DDL” to create preference, following is the PL/SQL of creating preference:

begin
    ctx_ddl.drop_preference(‘my_global_lexer’);
    ctx_ddl.drop_preference(‘my_english_lexer’);
    ctx_ddl.drop_preference(‘my_chinese_lexer’);
    ctx_ddl.create_preference(‘my_english_lexer’, ‘basic_lexer’);
    ctx_ddl.set_attribute(‘my_english_lexer’, ‘index_themes’, ‘yes’);
    ctx_ddl.set_attribute(‘my_english_lexer’,'theme_language’, ‘english’);    
    ctx_ddl.create_preference(‘my_chinese_lexer’, ‘chinese_vgram_lexer’);
    ctx_ddl.create_preference(‘my_global_lexer’, ‘multi_lexer’);
    ctx_ddl.add_sub_lexer(‘my_global_lexer’, ‘default’, ‘my_english_lexer’);
    ctx_ddl.add_sub_lexer(‘my_global_lexer’, ’simplified chinese’, ‘my_chinese_lexer’, ‘chi’);

     ctx_ddl.drop_preference(‘my_multi_datastore’);
    ctx_ddl.create_preference(‘my_multi_datastore’, ‘MULTI_COLUMN_DATASTORE’);
    ctx_ddl.set_attribute(‘my_multi_datastore’, ‘columns’, ‘TITLE,SHORT_DESCRIPTION,LONG_DESCRIPTION,KEY_WORDS’);
    
    commit;
end;

Next, we will use the preferences we created before to create the index:

create index my_multilanguageIndex on SEARCH_TEST(DUMMY_COLUMN1)
    indextype is ctxsys.context
    parameters (‘DATASTORE my_multi_datastore
        FILTER CTXSYS.NULL_FILTER
        LEXER my_global_lexer language column LANGUAGE_ID’);



OK, now we can use Query Operation in SQL to search content.

SQL> select id, score(1) score from citisg_user.CITI_LITESEARCH_TEST where CONTAINS (dummy_column1, ‘tsunami’,1) > 0 ;
id     score
—-   ——
1       14

SQL> select id, score(1) score from citisg_user.CITI_LITESEARCH_TEST where CONTAINS (dummy_column2, ‘海啸’,1) > 0 ;
id     scroe
—- ——–
2       12



Tips:

you should make sure the charset of content is same as the charset of oracle. Otherwise  you should use charset_filter to filter them.





2005年01月20日


Understanding Oracle Text:

Oracle Text use index to support searching. There are three different index types: Context, Catalog and Ctxxpath. They are used all for document indexing, but each has a different functionality.

Context index is a “domain” index used for fast retrieval of unstructured text. DML processing on a Context index is deferred. The actual index updates do not take place until an index SYNC is performed.

Catalog (CTXCAT) index is an online, “catalog” index, efficient for searching between small, simple text fields and with queries using some structured criteria, (usually numbers or dates). This index type supports only a basic functionality provided in a Context index. A Catalog index has all the characteristics of the normal database index.

Ctxxpath index is a special index installed during an Oracle Text install. This index uses Oracle Text code and can be created only on sys.xmltype columns. It is used to speed up certain queries using the existsnode method.

An Oracle text index can be created on the following column types: CHAR, VARCHAR, VARCHAR2, LONG, LONG RAW, BLOB, CLOB or BFILE.

Text index will store words and the documents in which these words occur. Oracle Text create an Index through following process:

 

The indexing process is managed in several stages called “classes”. Defined classes on the Oracle 8.1.7.2 database are:

SQL> select * from ctx_classes;
 
CLA_NAME                       CLA_DESCRIPTION
—————————— —————————–DATASTORE    Data store Class
FILTER                          Filter Class
SECTION_GROUP                   Section Group
LEXER                           Lexer Class
WORDLIST                        Word List Class
STOPLIST                        Stop List Class
STORAGE                         Storage Class
INDEX_SET                       Index Set

Datastore is a document data selector table from which the CTX engine reads column data and returns document data.

Filter takes the document data from the datastore class and filters it to readable text.

Sectioner takes a text format, as input, and generates two outputs (the section boundaries and plaintext).

Lexer gets plaintext from the sectioner and splits it into words (discrete tokens).

Every class has a defined list of objects, which are only templates.Instead of directly using objects, we are creating a “preference” from one of the object templates, which is further customized by setting “attributes”.

When we create the Oracle Text index, the indexing engine will read the defined preference for the index and invoke the attribute for each indexed document.

After creating Index, we can search the context using Oracle Text Query operators. Following is an example using CONTAINS operate to searching on CONTEXT index:

select id, score(1) score from citisg_user.CITI_LITESEARCH_TEST_STATIC where CONTAINS (doc_url, ‘tsunami’,1) > 0


Check Oracle Text installing:

Oracle Text is a part of Oracle installation. After installing, Oracle Text will create a role — CTXAPP, and an user — CTXSYS. All of the Oracle Text index information is kept in the database repository under user CTXSYS.  In order to manipulate Oracle Text, you should have the permission of CTXAPP, and some of operations even need you be CTXSYS user.

We can check for the existence of the objects under CTXSYS schema and the version of Oracle Text:

SQL> connect ctxsys/ctxsys
Connected.
SQL> select * from ctx_version;
 
VER_D
—–
8.1.7

SQL> select * from ctx_objects;
 
OBJ_CLASS           OBJ_NAME             OBJ_DESCRIPTION
—————   ——————- —————————————–
DATASTORE         DIRECT_DATASTORE      Documents are stored in the column
DATASTORE         DETAIL_DATASTORE      Documents are split into multiple lines
DATASTORE         FILE_DATASTORE        Documents are stored in files,column is file name
DATASTORE         URL_DATASTORE         Documents are web pages, column is URL
DATASTORE         USER_DATASTORE        Documents are stored in the column
DATASTORE         NESTED_DATASTORE      Documents are stored in a column in the nested table
DATASTORE     MULTI_COLUMN_DATASTORE    Documents are stored in multiple columns
FILTER            NULL_FILTER           Null filter
FILTER            USER_FILTER           User-defined filter
FILTER            CHARSET_FILTER        character set converting filter
FILTER            INSO_FILTER           filter for binary document formats
FILTER            PROCEDURE_FILTER      Procedure filter
SECTION_GROUP     NULL_SECTION_GROUP    null section group
SECTION_GROUP     BASIC_SECTION_GROUP   basic section group
SECTION_GROUP     HTML_SECTION_GROUP    html section group
SECTION_GROUP     XML_SECTION_GROUP     xml section group
SECTION_GROUP     NEWS_SECTION_GROUP    news section group
SECTION_GROUP     AUTO_SECTION_GROUP    auto section group
LEXER             BASIC_LEXER           Lexer for alphabetic languages
LEXER             JAPANESE_VGRAM_LEXER  V-gram lexer for Japanese
LEXER             KOREAN_LEXER          Dictionary-based lexer for Korean
LEXER             CHINESE_VGRAM_LEXER   V-GRAM lexer for Chinese
LEXER             CHINESE_LEXER         Chinese lexer
LEXER             MULTI_LEXER           Multi-language lexer
WORDLIST          BASIC_WORDLIST        basic wordlist
STOPLIST          BASIC_STOPLIST        basic stoplist
STOPLIST          MULTI_STOPLIST        multi-language stoplist
STORAGE           BASIC_STORAGE         text-index storage
INDEX_SET         BASIC_INDEX_SET       basic index set


An existence and the status of Text index is checked via user_indexes view:

SQL> select index_name name, index_type “TYPE 1″, ityp_owner “OWNER”,ityp_name “TYPE 2″, domidx_opstatus   status from user_indexes where ityp_name=”CONTEXT”

NAME          TYPE 1       OWNER        TYPE 2       STATUS
————  ———— ———— ———— ——–
KB_INDEX      DOMAIN       ARTIST       CONTEXT      VALID
WEB_TEXT      DOMAIN       WEB_ARTIST   CONTEXT      VALID
TEST_IDX      DOMAIN       CTXTEST      CONTEXT      INVALID
WEB_TEXT_GER  DOMAIN       WEB_ARTIST   CONTEXT      VALID

We have four Text indexes defined in the database. One of them is invalid.The following select will provide information about indexed columns and indexed documents:

SQL> select IDX_OWNER#,IDX_NAME,IDX_TABLE#,IDX_KEY_NAME, IDX_DOCID_COUNT from DR$INDEX;

IDX_OWNER# IDX_NAME              IDX_TABLE#  IDX_KEY_NAME             IDX_DOCID_COUNT
—————————————————————————————
       107 KB_INDEX            274239       ID,DOCU                   99014
       108 WEB_TEXT            276487       PRODUCT_DETAIL            99332
        81 TEST_IDX            191146       NR                        2
        97 WEB_TEXT_GER        238978       PRODUCT_DETAIL            97368

Listing content for the WEB_TEXT_GER index:

SQL> select token_text from ARTIST.DR$WEB_TEXT_GER$I;

TOKEN_TEXT
—————-
900782
387869
MANTEL
124
221
ANDEREN
EF
26.17
150005
AUFLOESUNGEN



Using Oracle Text:



Following is the standard steps to use Oracle Text searching engine :
1. Create the table that can support oracle text;
2. According to the source you will search on ,create the appropriate preference;
3. using preferences , create index on column you will searched  on;
4. use SQL searching the content;

following is a simple example :

First, we create a table whose column will be searched, and insert some data:

CREATE TABLE SEARCH_TEST (
  ID              VARCHAR2 (30)  NOT NULL,
  TITLE                  VARCHAR2 (80),
  DESCRIPTION       VARCHAR2 (1000),
  DUMMY_COLUMN1         VARCHAR2 (5),
  PRIMARY KEY ( ID )  );

insert into SEARCH_TEST (ID,TITLE,DESCRIPTION) values(1, ‘tsunami hit the eaten asia’,'DEC. 26 2004, the tsunami hit the easten asia and cause great damage.’);


Next, because it is a simple index, we will use predefined preference.

Then, create the index on the column “TITLE”:
  create index citisg_user.my_simpleindex on citisg_user.CITI_LITESEARCH_TEST(title)
    indextype is ctxsys.context
    parameters (‘DATASTORE CTXSYS.DEFAULT_DATASTORE
        FILTER CTXSYS.NULL_FILTER
        LEXER CTXSYS.DEFAULT_LEXER’);


Last, you can search the content in title column:

SQL> select id, score(1) score from citisg_user.CITI_LITESEARCH_TEST where CONTAINS (title, ‘tsunami’,1) > 0 ;

id         score
——–   ——-     
1             5












2005年01月11日

In Weblogic server6.1, we want to implement compression filter in order to increase the preformance. But, we encounter a big problem which may make the compression filter unfeasible.


This problem is the unmatched sequence of compression/decompression and encoding/decoding between appServer and browser side.


At browser side, regardless which kind of browser you use,  the browser will decompress the http body first if the head “content-encoding” is “gzip”. Then, the browser will read the decompressed content as charset set by head “content-type”.


At AppServer side, which is weblogic6.1 here,  our compression filter will compress the content first. Then, weblogic6.1 will encoding the compressed content to charset which our jsp claimed.


So, the flow is like following, which is unmatched:
             decoding    <– decompressing    <=======      encoding <– compressing


The result is that, if the charset is not default charset, i.e.ISO8859-1,  the compressed content will be encoded which will destroy the gzip format. For example,  the gzip content should begin with the magic code 31, 139, but, if jsp set the charset to UTF-8, two bytes
“31,139″ will be encoded to  three bytes “31, 194,139″. When the browser get the response , it found the first two byte is not the correct gzip magic code and show the blank page.



here is the fragment of servletresponseImpl class  picked from weblogic’s sourefile:

    public void setHeader(String s, String s1)
    {
        if(isCommitted())
            return;
        if(s1 == null)
            s1 = “”;
        if(s.equals(“Content-Type”))
        {
            int i = s1.toUpperCase().indexOf(“CHARSET”);
            if(i != -1)
            {
                String s2 = null;
                int j = s1.indexOf(‘;’, i);
                int k = s1.indexOf(‘=’, i);
                if(j == -1)
                {
                    if(k != -1 && k < s1.length())
                        s2 = s1.substring(k + 1).trim();
                } else
                if(k != -1 && k < j)
                    s2 = s1.substring(k + 1, j).trim();
                s2 = HttpParsing.StripHTTPFieldValue(s2);
                if(s2 != null && s2.length() != 0)
                    setEncoding(s2);
            } else
            {
                String s3 = outputStream.getOutput().getOutput().getEncoding();
                if(s3 != null && !BytesToString.is8BitCharset(s3))


=======see here ==========>  setEncoding(“ISO-8859-1″);


            }
        }
        if(s.equalsIgnoreCase(“Content-Length”))
            try
            {
                setContentLength(Integer.parseInt(s1.trim()));
                return;
            }
            catch(NumberFormatException numberformatexception) { }
        headers.setHeader(s, s1);
    }



I think what weblogic6.1 done is correct, because wls should make sure that the response is really encoded as declared in response header.  But, it should offer a place/point to let the hook program to compress the encoded content.


following is a thread on bea dev2dev:
                    http://forums.bea.com/bea/thread.jspa?threadID=200079650