posts - 40,  comments - 7,  trackbacks - 0
  2006年8月22日
关于inode;


inode 译成中文就是索引节点。每个存储设备或存储设备的分区(存储设备是硬盘、软盘、U盘 ... ... )被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode呢,就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快的找到相对应的文件。

做个比喻,比如一本书,存储设备或分区就相当于这本书,Block相当于书中的每一页,inode 就相当于这本书前面的目录,一本书有很多的内容,如果想查找某部份的内容,我们可以先查目录,通过目录能最快的找到我们想要看的内容。虽然不太恰当,但还是比较形象。

当我们用ls 查看某个目录或文件时,如果加上-i 参数,就可以看到inode节点了;比如我们前面所说的例子;



[root@localhost ~]# ls -li lsfile.sh
2408949 -rwxr-xr-x 1 root root 7 04-21 12:47 lsfile.sh

lsfile.sh 的inode值是 2408949 ; 查看一个文件或目录的inode,要通过ls 命令的的 -i参数。


2.10 inode 相同的文件是硬链接文件;


在Linux 文件系统中,inode值相同的文件是硬链接文件,也就是说,不同的文件名,inode可能是相同的,一个inode值可以对应多个文件。理解链接文件并不难,看看例子就会了。在Linux中,链接文件是通过ln工具来创建的。


2.11 创建硬链接,硬链接和源文件关系;


用ln 创建文件硬链接的语法:



# ln 源文件 目标文件

下面我们举一个例子,在这个例子中,我们要为sun.txt 创建其硬链接sun002.txt。然后看一下sun.txt和sun002.txt的属性的变化;


[root@localhost ~]# ls -li sun.txt 注:查看sun.txt的属性;
2408263 -rw-r--r-- 1 root root 29 04-22 21:02 sun.txt 注:这是sun.txt的属性;
[root@localhost ~]# ln sun.txt sun002.txt 注:我们通过ln 来创建sun.txt的硬链接文件sun002.txt
[root@localhost ~]# ls -li sun* 注:我们列一下sun.txt 和sun002.txt
2408263 -rw-r--r-- 2 root root 29 04-22 21:02 sun002.txt
2408263 -rw-r--r-- 2 root root 29 04-22 21:02 sun.txt

我们可以看到sun.txt在没有创建硬链接文件sun002.txt的时候,其链接个数是1(也就是-rw-r--r--后的那个数值),创建了硬链接sun002.txt创建后,这个值变成了2。也就是说,我们每次为sun.txt创建一个新的硬链接文件后,其硬链接个数都会增加1。

inode值相同的文件,他们的关系是互为硬链接的关系。当我们修改其中一个文件的内容时,互为硬链接的文件的内容也会跟着变化。如果我们删除互为硬链接关系的某个文件时,其它的文件并不受影响。比如我们把sun.txt删除后,我们还是一样能看到sun002.txt的内容,并且sun02.txt仍是存在的。

可以这么理解,互为硬链接关系的文件,他们好象是克隆体,他们的属性几乎是完全一样;

下面的例子,我们把sun.txt删除,然后我们看一下sun002.txt 是不是能看到其内容。



[root@localhost ~]# rm -rf sun.txt
[root@localhost ~]# more sun002.txt

注意:硬链接不能为目录创建,只有文件才能创建硬链接。


2.12 软链接的创建,及软接与源文件的关系;


创建软链接(也被称为符号链接)的语法;



# ln -s 源文文件或目录 目标文件或目录

软链接也叫符号链接,他和硬链接有所不同,软链接文件只是其源文件的一个标记。当我们删除了源文件后,链接文件不能独立存在,虽然仍保留文件名,但我们却不能查看软链接文件的内容了。



[root@localhost ~]# ls -li linuxsir001.txt
2408274 -rw-r--r-- 1 root root 29 04-22 21:53 linuxsir001.txt
[root@localhost ~]# ln -s linuxsir001.txt linuxsir002.txt
[root@localhost ~]# ls -li linuxsir001.txt linuxsir002.txt
2408274 -rw-r--r-- 1 root root 29 04-22 21:53 linuxsir001.txt
2408795 lrwxrwxrwx 1 root root 15 04-22 21:54 linuxsir002.txt -> linuxsir001.txt

解释

上面的例子,首先我们查看 linuxsir001.txt 的属性,比如inode 、所属文件种类、创建或修改时间等... ...我们来对比一下:

首先 对比一下节点:两个文件的节点不同;
其次 两个文件的归属的种类不同 linuxsir001.txt是-,也就是普通文件,而linuxsir002.txt 是l,它是一个链接文件;
第三 两个文件的读写权限不同 linuxsir001.txt 是rw-r--r-- ,而linuxsir002.txt的读写权限是 rwxrwxrwx
第三 两者的硬链接个数相同;都是1
第四 两文件的属主和所归属的用户组相同;
第五 修改(或访问、创建)时间不同;

我们还注意到了linuxsir002.txt 后面有一个标记 ->,这表示linuxsir002.txt 是linuxsir001.txt的软链接文件。

值得我们注意的是:当我们修改链接文件的内容时,就意味着我们在修改源文件的内容。当然源文件的属性也会发生改变,链接文件的属性并不会发生变化。当我们把源文件删除后,链接文件只存在一个文件名,因为失去了源文件,所以软链接文件也就不存在了。这一点和硬链接是不同的;



[root@localhost ~]# rm -rf linuxsir001.txt 注:删除linuxsir001.txt
[root@localhost ~]# ls -li linuxsir002.txt 注:查看linuxsir002 的属性;
2408795 lrwxrwxrwx 1 root root 15 04-22 21:54 linuxsir002.txt -> linuxsir001.txt
[root@localhost ~]# more linuxsir002.txt 注:查看linuxsir002.txt的内容;
linuxsir002.txt: 没有那个文件或目录 注:得到提示,linuxsir002.txt不存在。

上面的例子告诉我们,如果一个链接文件失去了源,就意味着他已经不存在了;

我们可以看到软链接文件,其实只是源文件的一个标记,当源文件失去时,他也就是存在了。软链接文件只是占用了inode来存储软链接文件属性等信息,但文件存储是指向源文件的。

软件链接,可以为文件或目录都适用。无论是软链接还是硬链接,都可以用rm来删除。rm工具是通用的。
参考资料:http://techcenter.dicder.com/2006/0908/content_185.htm
posted @ 2007-07-13 09:54 Lansing 阅读(720) | 评论 (0)编辑 收藏
写一些关于PL/SQL的语法,免得等到用到的时候还要去乱翻。
1。控制流程(if,while)
2。循环(for)
3。游标(cursor)
4。异常处理(exception)

1。控制流程()


A.条件语句
IF <statement> THEN
   PL/SQL
END IF;

IF <statement> THEN
   PL/SQL
ELSE
   PL/SQL
END IF;

IF
<statement> THEN
   PL/SQL
ELSIF <statement> THEN
   PL/SQL
END IF;


2。循环

A.simple loop
LOOP
   SQL
   EXIT WHEN <statement>;
END LOOP;

LOOP
   SQL
   IF
<statement> THEN
   EXIT;
   END IF;
END LOOP;

B.While loop
WHILE <statement> LOOP
   SQL
END LOOP;

C.For loop
FOR $counter in $low .. $high LOOP
   SQL
END LOOP


3。游标

在PL/SQL程序中定义的游标叫做显式游标。

A.显式游标的处理由四个部分组成:
cursor $cursorname is $Query;   --定义游标
open $cursorname;         --打开游标
fetch $cursorname into $othervariable  --把游标中的东西取出
close $cursorname    --关闭游标

B.游标属性
%found         布尔型属性,当最近一次读纪录成功
,为true.
%nofound                失败时,为false.
%isopen
%rowcount      返回已从游标中读取的记录数。

C.参数化游标

所有的SQL语句在上下文区内部都是可执行的,因此都有一个游标指向上下文区,此游标就是所谓的SQL游标。
与显式游标不同,SQL游标不被程序打开和关闭。


4.异常处理概念

异常处理是用来处理正常执行过程中未预料的事件。如果PL/SQL程序块一旦产生异常而又没有指出如何处理时,程序会自动终止。
异常处理部分放在PL/SQL的后半部分,结构为:

EXCEPTION
       WHEN first_exception THEN <code to handle first exception>
       WHEN second_exception THEN <code to handle second exception>
       WHEN OTHERS THEN <
code to handle second exception >  --OTHERS必须放在最后

异常分为预定义和用户定义两种。
用户定义的异常是通过显式使用RAISE语句来引发。如

DECLARE
  e_TooManyStudents EXCEPTION;  -- 类型为Exception,用于指示错误条件
  v_CurrentStudents NUMBER(3);  -- HIS-101学生注册当前号
  v_MaxStudents NUMBER(3);      -- HIS-101学生注册允许的最大号

BEGIN
 /* 找出注册学生当前号和允许的最大号 */

  SELECT current_students, max_students

    INTO v_CurrentStudents, v_MaxStudents

    FROM classes

    WHERE department = 'HIS' AND course = 101;

  /* 检查学生的号 */

  IF v_CurrentStudents > v_MaxStudents THEN

/* 太多的学生注册,则触发例外处理 */

  RAISE e_TooManyStudents;

  END IF;

EXCEPTION

  WHEN e_TooManyStudents THEN

    /* 当太多的学生注册,就插入信息解释发生过错误 */

    INSERT INTO log_table (info) VALUES ('History 101 has ' || v_CurrentStudents ||

      'students: max allowed is ' || v_MaxStudents);

END;


END;

用户定义的的异常处理
可以使用RAISE_APPLICATION_ERROR来创建自己的错误处理:
RAISE_APPLICATION_ERROR(error_number,error_message,[keep_errors]);
其中
error_number是从-20000到-20999之间的参数; error_message是相应的提示信息,小于512字节。如:

CREATE OR REPLACE PROCEDURE Register (
p_StudentID IN students.id%TYPE,
p_Department IN classes.department%TYPE,
p_Course IN classes.course%TYPE) AS
v_CurrentStudents NUMBER;  -- 班上学生的当前号
v_MaxStudents NUMBER;      -- 班上学生的最大号

BEGIN
/* 找出学生的当前号和最大号 */
SELECT current_students, max_students
 INTO v_CurrentStudents, v_MaxStudents
FROM classes
WHERE course = p_Course
AND department = p_Department;

/* 确认另外的学生是否有足够的教室 */
IF v_CurrentStudents + 1 > v_MaxStudents THEN
RAISE_APPLICATION_ERROR(-20000, 'Can''t add more students to ' ||
p_Department || ' ' || p_Course);
END IF;

/* 加一个学生在本班 */
ClassPackage.AddStudent(p_StudentID, p_Department, p_Course);

EXCEPTION
WHEN NO_DATA_FOUND THEN
   
RAISE_APPLICATION_ERROR(-20001, p_Department || ' ' || p_Course ||
         
' doesn''t exist!');
END Register;

posted @ 2007-01-23 10:10 Lansing 阅读(359) | 评论 (0)编辑 收藏
关于ODBC数据源连接文本 

              在《外部数据库的连接原理》一讲中我们说过,ODBC提供对多种数据库的支持,如dBase、Access、MS SQL 
            Server及Oracle,也就是说运用ODBC数据源中所提供的连接代码,我们可以实现对多种数据库的连接。以连接Access数据库为例,ODBC数据源连接文本的格式是:
              “Driver={数据库驱动程序};Dbq=数据库文件;”
              在以上连接文本中,如果数据库跟程序在同一目录下,或者用变量DefaultDir指定了数据库所在目录,则数据库文件可以不用全路径名,如下即可:
              “ODBC;DBQ=MSAccess.mdb;Driver={Microsoft Access Driver (*.mdb)};”
              如下也可:
              “ODBC;DBQ=MSAccess.mdb;DefaultDir=d:\Downloads\e21;Driver={Microsoft 
            Access Driver (*.mdb)};”
              如果数据库跟程序不在同一目录下,或者没有用变量DefaultDir指定数据库所在目录,则数据库文件需要用全路径名,如下:
              “ODBC;DBQ=E:\Quake III Arena\MSAccess.mdb;Driver={Microsoft Access 
            Driver (*.mdb)};”
              以上所说的是连接Access数据库的格式,那么连接其他数据库的ODBC数据源连接文本又是怎样的?连接不同类型的数据库要使用不同的对应驱动程序,没忘记吧!不同的驱动程序当然它们的参数组合也就不同了,每一种不同驱动程序都有其特定的的参数形式: 

              ⑴、MS Access ODBC DSNless 连接:
              ☆、参数:Driver 设置值:{Microsoft Access Driver (*.mdb)}
              ☆、参数:Dbq 设置值:实际路径文件名称
              ☆、例句:
              “Driver={Microsoft Access Driver 
            (*.mdb)};Dbq=c:\somepath\dbname.mdb;Uid=Admin;Pwd=pass; ”
              ⑵、dBase ODBC DSNless 连接: 
              ☆、参数:Driver 设置值:{Microsoft dBASE Driver (*.dbf)}
              ☆、参数:Dbq 设置值:实际路径文件名称
              ☆、例句:
              “Driver={Microsoft dBASE Driver 
            (*.dbf)};DriverID=277;Dbq=c:\somepath\dbname.dbf; ”
              ⑶、Oracle ODBC DSNless 连接:
              ☆、参数:Driver 设置值:{Microsoft ODBC for Oracle}
              ☆、参数:Dbq 设置值:实际路径文件名称
              ☆、例句:
              “Driver={Microsoft ODBC for 
            Oracle};Server=OracleServer.world;Uid=admin;Pwd=pass; ”
              ⑷、MS SQL Server DSNless 连接: 
              ☆、参数:Driver 设置值:{SQL Server};
              ☆、参数:Server 设置值:服务器名称
              ☆、参数:Database 设置值:数据表名称
              ☆、参数:Uid 设置值:用户名称
              ☆、参数:Pwd 设置值:密码
              ☆、例句:
              “Driver={SQL 
            Server};Server=servername;Database=dbname;Uid=sa;Pwd=pass; ”
              ⑸、MS Text Driver DSNless 连接: 
              ☆、参数:Driver 设置值:{Microsoft Text Driver (*.txt; *.csv)}
              ☆、参数:Dbq 设置值:实际路径文件名称
              ☆、例句:
              “Driver={Microsoft Text Driver (*.txt; 
            *.csv)};Dbq=c:\somepath\;Extensions=asc,csv,tab,txt;Persist Security 
            Info=False; ”
              ⑹、Visual Foxpro DSNless 连接:
              ☆、参数:Driver 设置值:{Microsoft Visual FoxPro Driver}
              ☆、参数:SourceType 设置值:DBC
              ☆、参数:SourceDB 设置值:实际路径文件名称 
              ☆、例句:
              “Driver={Microsoft Visual FoxPro 
            Driver};SourceType=DBC;SourceDB=c:\somepath\dbname.dbc;Exclusive=No;” 

              ⑺、MySQL DSNless 连接:
              ☆、参数:Driver 设置值:{mysql}
              ☆、参数:database 设置值:数据表名称
              ☆、参数:uid 设置值:用户名称
              ☆、参数:pwd 设置值:密码
              ☆、例句: 
              “driver={mysql}; 
            database=yourdatabase;uid=username;pwd=password;option=16386”
            *******************************************************************
            SQL语言简介 

              在上一讲中我们介绍了连接外部数据库的方法,那么连接之后怎样对外部数据库进行读取、显示、增删、更新、查询等操作呢?这些操作需要通过外部数据库等对象调用SQL指令才能完成。
              ㈠、什么是SQL语言
              SQL(Structure Query Languge,结构化查询语言)是一种数据库专用的计算机语言,不管是Oracle、MS 
            SQL 
            、Access、MySQL或其他公司的数据库,也不管数据库建立在大型主机或个人计算机上,都可以使用SQL语言来访问和修改数据库的内容。虽然不同公司的数据库软件多多少少会增加一些专属的SQL语法,但大体上,它们还是遵循ASNI(美国国家标准协会)制定的SQL标准。因为SQL语言具有易学习及阅读等特性,所以SQL逐渐被各种数据库厂商采用,而成为一种共通的标准查询语言。只要你学会SQL,即可操作各种数据库如Visual 
            Foxpro、Access、dBase等等。总之,SQL语言是各种数据库都可以使用的数据库查询语言。
              SQL语言不仅仅具有查询数据库的功能,而且可以对数据库完成选取、增删、更新与跳转等各种操作。
              ㈡、SQL语言的组成
              SQL语言是由命令(函数)、子句、运算符、加总函数及通配符等组成,分述如下:
              1、命令
              SQL的命令可分成数据定义语言与数据操作语言,数据定义语言可用来建立新的数据库、数据表、字段及索引等,本教程不予介绍;另一为数据操作语言,可用来建立查询表、排序、筛选数据、修改、增删等动作。数据定义语言命令常用的有选择、添加、删除和修改这四种:
              ⑴、命令:SELECT
              中文意思:选择
              说明:用于找出合乎条件的记录
              ⑵、命令:INSERT
              中文意思:插入
              说明:用于增加一笔记录或合并两个数据表
              ⑶、命令:UPDATE
              中文意思:更新
              说明:用于更正合乎条件的记录
              ⑷、命令:DELETE
              中文意思:删除
              说明:用于删除合乎条件的记录
              2、子句
              子句是用于设定命令要操作的对象(即参数),SQL所用的子句如下:
              ⑴、子句:FROM 
              中文意思:数据表
              说明:用于指定数据表
              ⑵、子句:WHERE
              中文意思:条件
              说明:用于设定条件
              ⑶、GROUP BY
              中文意思:分组(合并)
              说明:用于设定分组
              ⑷、ORDER BY
              中文意思:排序
              说明:用于设定输出的顺序及字段
              3、运算符
              子句参数中的运算符使子句构成不同的语法格式,如“字段1='100'”、“字段1>'100'”等。运算符又分逻辑运算符与比较运算符。
              ◇逻辑运算符如下:
              ⑴、运算符:AND
              中文意思:并且
              说明:逻辑且
              ⑵、运算符:OR 
              中文意思:或者
              说明:逻辑非
              ⑶、运算符:NOT
              中文意思:取反
              说明:逻辑非或逻辑反
              ◇比较运算符如下:
              ⑴、运算符:< 说明:小于
              ⑵、运算符:≤ 说明:小于等于
              ⑶、运算符:≥ 说明:大于等于
              ⑷、运算符:> 说明:大于
              ⑸、运算符:= 说明:等于
              ⑹、运算符:<> 说明:不等于
              ⑺、运算符:BETWEEN 说明:用于设定范围 中文意思:在...之间
              ⑻、运算符:LIKE 说明:用于通配设定 中文意思:如同
              ⑼、运算符:IN 说明:用于集合设定 中文意思:在...之内
              4、加总函数
              加总函数常常运用在命令的参数中,如:“SELECT SUM(数学),AVG(数学) FROM 成绩单”。
              ⑴、加总函数:AVG 
              中文意思:平均
              说明:用于求指定条件的平均 
              ⑵、加总函数:COUNT
              中文意思:数量
              说明:用于求指定的数量
              ⑶、加总函数:SUM
              中文意思:和
              说明:用于求指定条件的和
              ⑷、加总函数:MAX
              中文意思:最大值
              说明:用于求指定条件的最大值
              ⑸、加总函数:MIN
              中文意思:最小值
              说明:用于求指定条件的最小值
              5、通配符
              ⑴、通配符:% 意义:任何长度的字符串(包括0)
              ⑵、通配符:_ 意义:下划线表示任何一个字符
              ⑶、通配符:[] 意义:中括号表示某个范围内的一个字符
              在下一讲将说明SQL语言是怎样把命令(函数)、子句、运算符、及加总函数等组合在一起的。

            *************************************************************************

            嵌入式SQL的应用 

              SQL语句可以单独在数据库系统本身中执行,但如果运用在其他编程工具所编制的程序中,一般不能单独执行,而要把SQL语句嵌入到高级语言(如易语言)中使用,通过高级语言的命令和方法来调用之,此时SQL称为嵌入式SQL。调用SQL语句的程序称为宿主程序,在易语言中一般是把SQL语句作为宿主程序的唯一参数来直接处理。嵌入式SQL在使用上有一些规定,在易语言中目前的版本规定如下:
              ⑴、在程序中要区分SQL语句和宿主语言的语句。在易语言中好区分,因为SQL语句形式是英文的,而易语言是中文的,但在实际应用时仍然有可能会混乱,所以易语言要把SQL语句转化为文本型才能调用,即嵌入式SQL语句两边要用双引号来标示。
              ⑵、允许SQL语句使用宿主程序的变量,但使用时要将宿主程序的变量跟外部数据库中表格的字段名区别开来,区别方法如下:
              ①、在易语言中要将变量类型转化为文本型变量才能被SQL文本相加使用,比如下面的例子中有一个叫“数字1”的整数类型变量,插入到SQL文本中是这样表达:
              外部数据库1.查询 (“select * from chj where ” + 组合框1.内容 + “=” + 到文本 (数字1))
              ②、包含字段名的SQL文本两边加双引号,变量名不能在双引号内,如上例。
              ⑶、要将字段名跟字段值区别开来,区别方法如下:
              ①、对于文本类型的字段,在其字段值两边要加上“'”号标示其文本值,代表语法是:字段名称=‘文本值’。如下:
              外部数据库1.查询 (“select * from chj where 姓名='山大王'”)
              又如下面“查找编辑框.内容”中的字段值是文本型,嵌入式SQL语句如下:
              外部数据库1.查询 (“select * from chj where 姓名==” + “'” + 查找编辑框.内容 + 
“'”)
              ②、对于数字类型的字段,在SQL语句中表示其字段值,两边不加符号标示,代表语法是:字段名称=数字值。如下两例:
              外部数据库1.查询 (“select * from chj where ” + 组合框1.内容 + “=” + 查找编辑框.内容) 
              外部数据库1.查询 (“select * from chj where 学号=17”)
              ③、对于日期时间类型的字段,在其字段值两边要加上“#”号标示其时间值,代表语法是:字段名称=#时间值#。如下两例:
              外部数据库1.查询 (“select * from chj where 入学时间 BETWEEN #2001-01-01# and 
            #2002-01-01#”)
              外部数据库1.查询 (“select * from chj where ” + 组合框1.内容 + “=” + “#” + 
            查找编辑框.内容 + “#”)
              ④、也可以将SQL语句中的字段名(尤其是中文名)可用中括号括住,如:[字段名]。
              
              ⑷、SQL语句要用半角输入法输入,否则可能会出错。
              那么在易语言中怎样调用SQL语句呢?一般是在外部数据库对象(控件)的方法中调用,试概括如下:
              ⑴、对外部数据库进行查询的方法。
              对外部数据库的查询就是在对外部数据库不加编辑改动的前提下,只通过记录集来对数据库进行显示、查询、筛选、排序和记录集的合并等操作。
              所有查询类的方法起源于下面这个语句,其他查询类语句是对这个语句的调用(将此语句作为唯一的参数),该语句如下:
              外部数据库.查询 (查询类SQL语句)
              也可这样表达:
              外部数据库.查询 (“SELECT...FROM...[WHERE]...[GROUP BY]...[ORDER BY]... ”)
              该方法是对当前被打开数据库进行数据查询,返回的结果称为“记录集句柄”(即记录集的标记)。注意当不再使用此记录集时,必须使用“关闭记录集”将其关闭,如果失败,返回0。在易语言中,将以上语句等同于记录集句柄以作为其他查询类语句的参数。为了使该参数在所有子程序中都能应用,我们一般把它设置为整数型全局变量,并将其值设置如下:
              记录集句柄=外部数据库.查询 (查询类SQL语句)
              由于易语言要把SQL语句转化为文本型才能调用,所以嵌入式SQL语句两边要有双引号,例句:
              记录集句柄 = 外部数据库1.查询 (“select * from chj ”)
              ※ “chj”是外部数据库中一个表的名称
              又如,欲得到排序的记录集,应象下面这样赋值:
              记录集句柄 = 外部数据库1.查询 (“SELECT * FROM chj ORDER BY 语文 DESC”)
              现将外部数据库控件中其他的查询类方法列举如下:
              ①、外部数据库.重新查询 (记录集句柄) 即:
              外部数据库.重新查询 (外部数据库.查询 (查询类SQL语句))
              例句:外部数据库1.重新查询 (外部数据库1.查询 (“select * from chj ”)) 
              ②、外部数据库.首记录前 (记录集句柄) 即:
              外部数据库.首记录前 (外部数据库.查询 (查询类SQL语句))
              例句:外部数据库1.首记录前 (记录集句柄)
              ③、外部数据库.尾记录后 (记录集句柄)
              ④、外部数据库.到首记录 (记录集句柄)
              ⑤、外部数据库.到尾记录 (记录集句柄)
              ⑥、外部数据库.到前一记录 (记录集句柄)
              ⑦、外部数据库.到后一记录 (记录集句柄)
              ⑧、外部数据库.读 (记录集句柄,字段名称或位置)
              例句:语文编辑框.内容 = 到文本 (外部数据库1.读 (记录集句柄, “语文”))
              ⑵、对外部数据库进行编辑的方法。
              所谓对外部数据库的编辑,就是变更改动外部数据库本身,包括添加、更新、删除等,对数据库进行编辑不必通过记录集。所有非查询类SQL语句都嵌入下面这个语句来执行:
              外部数据库.执行 (非查询类SQL语句)
              ①、添加记录,其语法如下:
              外部数据库.执行 (“insert into 表名称(字段1,字段2...) values (字段值1,字段值2...) ”)
              例句:
              外部数据库1.执行 (“INSERT INTO chj ” + “(学号,姓名,语文,数学,英语)” + “ valueS ” + 
            “(” + 学号编辑框.内容 + “,'” + 姓名编辑框.内容 + “','” + 语文编辑框.内容 + “','” + 
            数学编辑框.内容 + “','” + 英语编辑框.内容 + “')”)
              ②、更新记录,其语法如下:
              外部数据库.执行 (“UPDATE 表名称 SET 字段1=字段值1,字段2=字段值2...WHERE 条件式”) 
              例句:
              外部数据库1.执行 (“UPDATE chj SET 学号=” + “'” + 学号编辑框.内容 + “',” + “姓名=” 
            + “'” + 姓名编辑框.内容 + “',” + “语文=” + “'” + 语文编辑框.内容 + “',” + “数学=” 
            + “'” + 数学编辑框.内容 + “',” + “英语=” + “'” + 英语编辑框.内容 + “' ” + “WHERE 
            姓名=” + “'” + 姓名1 + “' ” + “AND 语文=” + 语文1 + “AND 数学=” + 数学1 + “AND 
            英语=” + 英语1 + “AND 学号=” + 学号1)
              ③、删除记录,其语法如下:
              外部数据库.执行 (“DELETE * FROM 表名称 WHERE 条件式”)
              例句:
              外部数据库.执行 (“外部数据库1.执行 (“DELETE * FROM chj ” + “WHERE 姓名=” + “'” + 
            姓名1 + “' ” + “AND 语文=” + 语文1 + “AND 数学=” + 数学1 + “AND 英语=” + 英语1 + 
            “AND 学号=” + 学号1)”)
posted @ 2007-01-19 12:17 Lansing 阅读(1575) | 评论 (3)编辑 收藏
Java虚拟机

一、什么是Java虚拟机


Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等,还具有相应的指令系统。


1.为什么要使用Java虚拟机


Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。


2.谁需要了解Java虚拟机


Java虚拟机是Java语言底层实现的基础,对Java语言感兴趣的人都应对Java虚拟机有个大概的了解。这有助于理解Java语言的一些性质,也有助于使用Java语言。对于要在特定平台上实现Java虚拟机的软件人员,Java语言的编译器作者以及要用硬件芯片实现Java虚拟机的人来说,则必须深刻理解Java虚拟机的规范。另外,如果你想扩展Java语言,或是把其它语言编译成Java语言的字节码,你也需要深入地了解Java虚拟机。


3.Java虚拟机支持的数据类型


Java虚拟机支持Java语言的基本数据类型如下:


byte://1字节有符号整数的补码
short://2字节有符号整数的补码
int://4字节有符号整数的补码
long://8字节有符号整数的补码
float://4字节IEEE754单精度浮点数
double://8字节IEEE754双精度浮点数
char://2字节无符号Unicode字符


几乎所有的Java类型检查都是在编译时完成的。上面列出的原始数据类型的数据在Java执行时不需要用硬件标记。操作这些原始数据类型数据的字节码(指令)本身就已经指出了操作数的数据类型,例如iadd、ladd、fadd和dadd指令都是把两个数相加,其操作数类型别是int、long、float和double。虚拟机没有给boolean(布尔)类型设置单独的指令。boolean型的数据是由integer指令,包括integer返回来处理的。boolean型的数组则是用byte数组来处理的。虚拟机使用IEEE754格式的浮点数。不支持IEEE格式的较旧的计算机,在运行Java数值计算程序时,可能会非常慢。


虚拟机支持的其它数据类型包括:
object//对一个Javaobject(对象)的4字节引用
returnAddress//4字节,用于jsr/ret/jsr-w/ret-w指令
注:Java数组被当作object处理。


虚拟机的规范对于object内部的结构没有任何特殊的要求。在Sun公司的实现中,对object的引用是一个句柄,其中包含一对指针:一个指针指向该object的方法表,另一个指向该object的数据。用Java虚拟机的字节码表示的程序应该遵守类型规定。Java虚拟机的实现应拒绝执行违反了类型规定的字节码程序。Java虚拟机由于字节码定义的限制似乎只能运行于32位地址空间的机器上。但是可以创建一个Java虚拟机,它自动地把字节码转换成64位的形式。从Java虚拟机支持的数据类型可以看出,Java对数据类型的内部格式进行了严格规定,这样使得各种Java虚拟机的实现对数据的解释是相同的,从而保证了Java的与平台无关性和可
移植性。


二、Java虚拟机体系结构


Java虚拟机由五个部分组成:一组指令集、一组寄存器、一个栈、一个无用单元收集堆(Garbage-collected-heap)、一个方法区域。这五部分是Java虚拟机的逻辑成份,不依赖任何实现技术或组织方式,但它们的功能必须在真实机器上以某种方式实现。


1.Java指令集


Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器,子程序转移等。Java指令集相当于Java程序的汇编语言。
Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。


虚拟机的内层循环的执行过程如下:


do{
取一个操作符字节;
根据操作符的值执行一个动作;
}while(程序未结束)


由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为:


第一个字节*256+第二个字节字节码指令流一般只是字节对齐的。指令tabltch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。


2.寄存器


Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。


Java虚拟机的寄存器有四种:
pc:Java程序计数器。
optop:指向操作数栈顶端的指针。
frame:指向当前执行方法的执行环境的指针。
vars:指向当前执行方法的局部变量区第一个变量的指针。


Java虚拟机


Java虚拟机是栈式的,它不定义或使用寄存器来传递或接受参数,其目的是为了保证指令集的简洁性和实现时的高效性(特别是对于寄存器数目不多的处理器)。
所有寄存器都是32位的。


3.栈


Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。


(1)局部变量区 每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如,一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n+1所代表的存储空间。)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。


(2)运行环境区 在运行环境中包含的信息用于动态链接,正常的方法返回以及异常传播。


·动态链接
运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。


·正常的方法返回
如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。


·异常和错误传播
异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用


·程序使用了throw语句。
当异常发生时,Java虚拟机采取如下措施:
·检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。
·与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。
·由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。
·如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误传播将被继续下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。
(3)操作数栈区 机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果。例如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。


每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。


4.无用单元收集堆


Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java语言具有无用单元收集能力:它不给程序员显式释放对象的能力。Java不规定具体使用的无用单元收集算法,可以根据系统的需求使用各种各样的算法。


5.方法区


方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在无用单元收集堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。

posted @ 2007-01-18 14:27 Lansing 阅读(578) | 评论 (1)编辑 收藏
Sun 提供的标准 Java 开发包(JDK)没有提供创建特定于平台的可执行文件的工具(一点都不吃惊,这是真的)。然而,其实有很多方法能够帮助你实现这一想法。
  
  第三方工具
  一种方法是使用第三方商业工具或免费工具将 Java 应用程序打包为一个可执行文件。
  
  下面是价格和特性都不同的两个工具,但是在 Web 上还有其它几个第三方工具可以免费下载。
  
  http://www.bysoft.se/sureshot/exej/
  http://www.duckware.com/jexepack/
  使用商业安装程序(installer)
  InstallAnywhere 是一个常用的安装程序,它将管理应用程序的安装过程,并将应用程序打包为可执行程序。
  
  使用 .jar
  除了以上方法之外,还可以将应用程序打包为一个可执行的 .jar 文件,而不是一个 .exe 文件。在这篇文章中我将不详细介绍这种方法,你可以在这里找到一个非常棒的在线教程
  
  你需要做的最重要的一件事是指定在 .jar 文件中哪个类是应用程序的入口点。例如,对你的应用程序来说就是具有一个 public static void main(String[] args) 方法的引导类。可以在 .jar 表示文件的 Main-Class 头部信息中提供这些信息。这个头部信息的通用形式为:Main-Class: classname,其中 classname 是应用程序的入口点的类名称。
  
  使用 Java Webstart
  Java Webstart 是标准 Java 运行时环境(JRE)的隐藏的宝物,自从版本 1.3 开始,JRE 就包含了 Java Webstart。它是一个简单但功能强大且灵活的将应用程序部署到任何平台的方法。
  
  Webstart 允许应用程序的用户从他们的浏览器、电子邮件或桌面启动和管理应用程序。Java Webstart 的一个主要优点是一旦应用程序被安装,在每次启动它时,它都将会检查用户是否在运行最新版本的应用程序。如果不是,应用程序将通过网络装载新版本到桌面然后执行,因此解决了软件传播问题。
  
  如果你的应用程序已经有很多用户的话,这一点就尤其重要。还有很重要的一点是,它能够检查用户的本地桌面环境,并能保证他们安装了正确的 JRE 版本来运行你的应用程序。
  
  Java Webstart 本身有一系列文章,所以我建议你访问 Java Webstart Web 站点查看更多文档和教程。
  
  结束语
  前两种方法可能会满足你对这个问题的需要,但是我强烈建议你仔细看一下 Java Webstart。它是 Java 标准的一部分,并且能够在所有平台下一致工作。我比较喜欢这个应用程序打包方法。
posted @ 2007-01-18 14:21 Lansing 阅读(688) | 评论 (0)编辑 收藏
     摘要: Lucene In Action ch 6(I) 笔记 --自定义排序 ----- 2006-2-16       使用 Lucene 来搜索内容,搜索结果的显示顺序当然是比较重要的.Lucene中Build-in的几个排序定义在大多数情况下是不适合我们使用的.要适合自己的应用程序的场景,就只能自定义排序功能,本节...  阅读全文
posted @ 2007-01-05 10:27 Lansing 阅读(700) | 评论 (0)编辑 收藏
     摘要: Lucene In Action ch 5 笔记 --高级搜索技术 ----- 2006-2-15 该章介绍了Lucene的一些高级技术,如 结果排序,搜索多个Index,过虑技术....下面就看看这些高级技巧吧. I.Sorting search results ...  阅读全文
posted @ 2007-01-05 10:25 Lansing 阅读(600) | 评论 (0)编辑 收藏
     摘要: Lucene In Action ch 4 笔记(I) -- Analysis ----- 2006-2-12 本章详细的讨论了 Lucene的分析处理过程和几个Analyzer. 在indexing过程中要把需要indexing的text分析处理一下, 经过处理和切词 然后建立index. 而不通的Ana...  阅读全文
posted @ 2007-01-05 10:14 Lansing 阅读(1064) | 评论 (0)编辑 收藏
     摘要: 1. 实现一个简单的search feature    在本章中只限于讨论简单Lucene 搜索API, 有下面几个相关的类:  Lucene 基本搜索API: 类 ...  阅读全文
posted @ 2007-01-05 10:11 Lansing 阅读(830) | 评论 (0)编辑 收藏
     摘要: Lucene In Action ch2 系统的讲解了 indexing,下面就来看看吧. 1,indexing 的处理过程.   首先要把indexing的数据转换为text,因为Lucene只能索引text,然后由Analysis来过虑text,把一些ch1中提到的所谓的stop words 过滤掉, 然后建...  阅读全文
posted @ 2007-01-05 10:10 Lansing 阅读(826) | 评论 (0)编辑 收藏

若人生是直线前进的,
那么命中注定有若干的交点,
认识注定的人,
欣赏注定的风景...

可人生还是有选择的,
那么道路也就多了些分叉口,
错过注定的人,
错过注定的风景...

若我曾经再某个分叉路口选错了方向却遇到了注定的人,该感谢上苍赐予我的福气吧!
可是人生又有了新的岔口,选择有些艰难,但是面对幸福我很坚定!

该怎么爱你

我听 过你的微笑
就忘了 所有动荡
当我靠过你的 那双肩膀
也就忘了 尘土飞杨
我只是 一棵小草
孤单的 微不足道
当你搬进我的小小国度
我才相信 平凡的美好
该怎么爱你 我才不会忘
你的温度 来过身旁
就算哪一天 失去了方向
爱过的 完整的 还在心上
该怎么爱你 才可以盼望
那种幸福 远远流长
就怕这时间 让我赶不上
说一句 只一句 我曾到过天堂
我相信最灿烂的一秒
是守著你的眼光
不是热烈拥抱
不是惊涛骇浪
我相信最美丽的风光
是在彼此身旁
有另一双肩膀
一起慢慢变老

posted @ 2006-12-13 14:22 Lansing 阅读(300) | 评论 (0)编辑 收藏
 本文主要面向具体使用,适用于已熟悉java编程的lucene初学者。
1. Lucene的简介


1.1 Lucene 历史


      org.apache.lucene包是纯java语言的全文索引检索工具包。
      Lucene的作者是资深的全文索引/检索专家,最开始发布在他本人的主页上,2001年10月贡献给APACHE,成为APACHE基金jakarta的一个子项目。
      目前,lucene广泛用于全文索引/检索的项目中。
      lucene也被翻译成C#版本,目前发展为Lucene.Net(不过最近好象有流产的消息)。


1.2 Lucene 原理


       lucene的检索算法属于索引检索,即用空间来换取时间,对需要检索的文件、字符流进行全文索引,在检索的时候对索引进行快速的检索,得到检索位置,这个位置记录检索词出现的文件路径或者某个关键词。
       在使用数据库的项目中,不使用数据库进行检索的原因主要是:数据库在非精确查询的时候使用查询语言“like %keyword%”,对数据库进行查询是对所有记录遍历,并对字段进行“%keyword%”匹配,在数据库的数据庞大以及某个字段存储的数据量庞大的时候,这种遍历是致命的,它需要对所有的记录进行匹配查询。因此,lucene主要适用于文档集的全文检索,以及海量数据库的模糊检索,特别是对数据库的xml或者大数据的字符类型。


2.Lucene的下载和配置


2.1 Lucene的下载


       lucene在jakarta项目中的发布主页:http://jakarta.apache.org/lucene/docs/index.html。以下主要针对windows用户,其它用户请在上面的地址中查找相关下载。


       lucene的.jar包的下载(包括.jar和一个范例demo):
http://apache.oregonstate.edu/jakarta/lucene/binaries/lucene-1.4-final.zip


        lucene的源代码下载:
http://www.signal42.com/mirrors/apache/jakarta/lucene/source/lucene-1.4-final-src.zip


 lucene的api地址:http://jakarta.apache.org/lucene/docs/api/index.html


 本文使用lucene版本:lucene-1.4-final.jar。


2.2 lucene的配置


        首先请确定你的机子已经进行了java使用环境的基本配置,即确保在某个平台下能够运行java源代码,否则请查阅相关文档进行配置。
        接下来进入lucene的配置:
        普通使用者:在环境变量的CLASSPATH中添加lucene的位置。比如:“D:\java \lucene-1.4-final\lucene-1.4-final.jar;”。
       jbuilder使用者:在“Project”--“Project Properties”--“Required Libraries”进行添加。
       Jsp使用者:也可以直接将lucene-1.4-final.jar文件放到\WEB-INF\classes下。


3. Lucene 的范例(Demo )


3.1 Demo说明

     
        可以得到的Demo包括:lucene-demos-1.4-final、XMLIndexingDemo,lucene-demos-1.4-final中包括对普通文件和html文件的两种索引,XMLIndexingDemo针对xml文件的索引。他们的区别主要在于:对普通文件进行索引时只要对文件的全文进行索引,而针对html、xml文件时,对标签类型不能进行索引,在实现上:html、xml的索引需要额外的数据流分析器,以分析哪些内容有用哪些无用。因此,在后两者实现上,索引的时间额外开支,甚至超过索引本身时间,而检索时间没有区别。


        以上Demo中,lucene-demos-1.4-final自带于lucene-1.4-final.zip中,XMLIndexingDemo的下载地址:
http://cvs.apache.org/viewcvs.cgi/jakarta-lucene-sandbox/contributions/XML-Indexing-Demo/


3.2 Demo的运行


        首先将demo.jar的路径添加如环境变量的CLASSPATH中,例如:“D:\java\lucene-1.4-final\lucene-demos-1.4-final.jar;”,同时确保已经添加lucene-1.4-final.jar。


        然后进行文件的全文索引,在dos控制台中,输入命令“java org.apache.lucene.demo.IndexFiles {full-path-to-lucene}/src”,后面的路径为所要进行索引的文件夹,例如:“java org.apache.lucene.demo.IndexFiles c:\test”。


        接着对索引进行检索,敲入“java org.apache.lucene.demo.SearchFiles”,在提示“Query:”后输入检索词,程序将进行检索列出检索得到的结果(检索词出现的文件路径)。
 
       其他Demo的运行请参考\docs\demo.html。
       在运行Demo后请阅读Demo的源代码以便深入学习。

4. 利用Lucene进行索引


        进行lucene的熟悉后,我们将学习如何使用Lucene。
 一段索引的应用实例:

    //需要捕捉IOException异常
    //建立一个IndexWriter,索引保存目录为“index”
    String[] stopStrs = {
        "他奶奶的", "fuck"};
    StandardAnalyzer analyzer = new StandardAnalyzer(stopStrs);
    IndexWriter writer = new IndexWriter("index", analyzer, true);
   
    //添加一条文档
    Document doc = new Document();
    doc.add(Field.UnIndexed("id", "1"));//“id”为字段名,“1”为字段值
    doc.add(Field.Text("text", "fuck,他奶奶的,入门与使用"));
    writer.addDocument(doc);
   
    //索引完成后的处理
    writer.optimize();
    writer.close();

       看完这段实例后,我们开始熟悉lucene的使用:

4.1 Lucene的索引接口


 在学习索引的时候,首先需要熟悉几个接口:


4.1.1分析器Analyzer


        分析器主要工作是筛选,一段文档进来以后,经过它,出去的时候只剩下那些有用的部分,其他则剔除。而这个分析器也可以自己根据需要而编写。
        org.apache.lucene.analysis.Analyzer:这是一个虚构类,以下两个借口均继承它而来。


        org.apache.lucene.analysis.SimpleAnalyzer:分析器,支持最简单拉丁语言。


        org.apache.lucene.analysis.standard.StandardAnalyzer:标准分析器,除了拉丁语言还支持亚洲语言,并在一些匹配功能上进行完善。在这个接口中还有一个很重要的构造函数:StandardAnalyzer(String[] stopWords),可以对分析器定义一些使用词语,这不仅可以免除检索一些无用信息,而且还可以在检索中定义禁止的政治性、非法性的检索关键词。


4.1.2 IndexWriter


        IndexWriter的构造函数有三种接口,针对目录Directory、文件File、文件路径String三种情况。
例如IndexWriter(String path, Analyzer a, boolean create),path为文件路径,a为分析器,create标志是否重建索引(true:建立或者覆盖已存在的索引,false:扩展已存在的索引。)
       一些重要的方法:

接口名

备注

addDocument(Document doc)

索引添加一个文档

addIndexes(Directory[] dirs)

将目录中已存在索引添加到这个索引

addIndexes(IndexReader[] readers)

将提供的索引添加到这个索引

optimize()

合并索引并优化

close()

关闭


       IndexWriter为了减少大量的io维护操作,在每得到一定量的索引后建立新的小索引文件(笔者测试索引批量的最小单位为10),然后再定期将它们整合到一个索引文件中,因此在索引结束时必须进行wirter. optimize(),以便将所有索引合并优化。


4.1.3 org.apache.lucene.document


 以下介绍两种主要的类:
 a)org.apache.lucene.document.Document:
        Document文档类似数据库中的一条记录,可以由好几个字段(Field)组成,并且字段可以套用不同的类型(详细见b)。Document的几种接口:
 

接口名

备注

add(Field field)

添加一个字段(Field)到Document

String get(String name)

从文档中获得一个字段对应的文本

Field getField(String name)

由字段名获得字段值

Field[] getFields(String name)

由字段名获得字段值的集


 b)org.apache.lucene.document.Field
        即上文所说的“字段”,它是Document的片段section。
        Field的构造函数:
       Field(String name, String string, boolean store, boolean index, boolean token)。
        Indexed:如果字段是Indexed的,表示这个字段是可检索的。
        Stored:如果字段是Stored的,表示这个字段的值可以从检索结果中得到。
        Tokenized:如果一个字段是Tokenized的,表示它是有经过Analyzer转变后成为一个tokens序列,在这个转变过程tokenization中,Analyzer提取出需要进行索引的文本,而剔除一些冗余的词句(例如:a,the,they等,详见org.apache.lucene.analysis.StopAnalyzer.ENGLISH_STOP_WORDS和org.apache.lucene.analysis.standard.StandardAnalyzer(String[] stopWords)的API)。Token是索引时候的基本单元,代表一个被索引的词,例如一个英文单词,或者一个汉字。因此,所有包含中文的文本都必须是Tokenized的。
     Field的几种接口:

Name

Stored

Indexed

Tokenized

use

Keyword(String name,

        String value)

Y

Y

N

date,url

Text(String name, Reader value)

N

Y

Y

short text fields:

title,subject

Text(String name, String value)

Y

Y

Y

longer text fields,

like “body”

UnIndexed(String name,

String value)

Y

N

N

 

UnStored(String name,

         String value)

N

Y

Y

 

 


5. 利用Lucene进行检索


5.1 一段简单的检索代码

 

    //需要捕捉IOException,ParseException异常
    //处理检索条件
    Query query = QueryParser.parse("入门", "text", analyzer);

    //检索
    Searcher searcher = new IndexSearcher("./index");//"index"指定索引文件位置
Hits hits = searcher.search(query);

    //打印结果值集
    for (int i = 0; i < hits.length(); i++) {
      doc = hits.doc(i);
      String id = doc.get("id");
      System.out.println("found " + "入门" + " on the id:" + id);
}

 

5.2 利用Lucene的检索接口


5.2.1 Query与QueryParser


        主要使用方法:
QueryParser .parse(String query, String field, Analyzer analyzer),例如:
Query query = QueryParser.parse("入门", "text", analyzer);
"入门"为检索词, "text"为检索的字段名, analyzer为分析器


5.2.2 Hits与Searcher


       Hits的主要使用接口:
 

接口名

备注

Doc(int n)

返回第n个的文档的所有字段

length()

返回这个集中的可用个数

 


6. Lucene的其他使用


6.1 Lucene 的索引修改


        下面给出一段修改索引的代码,请根据Lucene的API解读:


  /**
   * 对已有的索引添加新的一条索引
   * @param idStr String:要修改的id
   * @param doc Document:要修改的值
   */
  public void addIndex(String idStr, String valueStr) {
    StandardAnalyzer analyzer = new StandardAnalyzer();
    IndexWriter writer = null;
    try {
      writer = new IndexWriter(indexPath, analyzer, false);
      writer.mergeFactor = 2; //修正lucene 1.4.2 bug,不添加则不合并原有索引

          Document doc = new Document();
          doc.add(Field.UnIndexed("id", idStr));//“id”为字段名,“1”为字段值
          doc.add(Field.Text("text", valueStr));
      writer.addDocument(doc);

      writer.optimize();
      writer.close();
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }

  /**
   * 删除索引
   *
   * @param idStr String
   */
  public void deleteIndex(String idStr) {
    try {
      Directory dirt = FSDirectory.getDirectory(indexPath, false);
      IndexReader reader = IndexReader.open(dirt);
      IndexXML.deleteIndex(idStr, reader);
      reader.close();
      dirt.close();
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }


6.2 Lucene 的检索结果排序


        Lucene的排序主要是对org.apache.lucene.search.Sort的使用。Sort可以直接根据字段Field生成,也可以根据标准的SortField生成,但是作为Sort的字段,必须符合以下的条件:唯一值以及Indexed。可以对Integers, Floats, Strings三种类型排序。
        对整数型的ID检索结果排序只要进行以下的简单操作:

 Sort sort = new Sort("id");
Hits hits = searcher.search(query, sort);

       用户还可以根据自己定义更加复杂的排序,详细请参考API。


7 总结


        Lucene给java的全文索引检索带来了非常强大的力量,以上仅对Lucene进行简单的入门说明。

posted @ 2006-11-08 14:59 Lansing 阅读(337) | 评论 (0)编辑 收藏

Lucene提供了方便您创建自建查询的API,也通过QueryParser提供了强大的查询语言。

本文讲述Lucene的查询语句解析器支持的语法,Lucene的查询语句解析器是使用JavaCC工

具生成的词法解析器,它将查询字串解析为Lucene Query对象。
项(Term)
一条搜索语句被拆分为一些项(term)和操作符(operator)。项有两种类型:单独项和

短语。
单独项就是一个单独的单词,例如"test" , "hello"。
短语是一组被双引号包围的单词,例如"hello dolly"。
多个项可以用布尔操作符连接起来形成复杂的查询语句(接下来您就会看到)。
注意:Analyzer建立索引时使用的解析器和解析单独项和短语时的解析器相同,因此选择

一个不会受查询语句干扰的Analyzer非常重要。luence1.4的StandardAnalyzer的解析器已

经支持中文等亚洲国家的文字了,可以直接。标准的解析其不支持中文。

域(Field)
Lucene支持域。您可以指定在某一个域中搜索,或者就使用默认域。域名及默认域是具体

索引器实现决定的。(怎么定制默认域?)
您可以这样搜索域:域名+":"+搜索的项名。
举个例子,假设某一个Lucene索引包含两个域,title和text,text是默认域。如果您想查

找标题为"The Right Way"且含有"don't go this way"的文章,您可以输入:
title:"The Right Way" AND text:go
或者
title:"Do it right" AND right
因为text是默认域,所以这个域名可以不行。
注意:域名只对紧接于其后的项生效,所以
title:Do it right
只有"Do"属于title域。"it"和"right"仍将在默认域中搜索(这里是text域)。

项修饰符(Term Modifiers)
Lucene支持项修饰符以支持更宽范围的搜索选项。
用通配符搜索
Lucene支持单个与多个字符的通配搜索。
使用符号"?"表示单个任意字符的通配。
使用符号"*"表示多个任意字符的通配。
单个任意字符匹配的是所有可能单个字符。例如,搜索"text或者"test",可以这样:

te?t
多个任意字符匹配的是0个及更多个可能字符。例如,搜索test, tests 或者 tester,可

以这样: test*
您也可以在字符窜中间使用多个任意字符通配符。 te*t
注意:您不能在搜索的项开始使用*或者?符号。

模糊查询
Lucene支持基于Levenshtein Distance与Edit Distance算法的模糊搜索。要使用模糊搜索

只需要在单独项的最后加上符号"~"。例如搜索拼写类似于"roam"的项这样写:
roam~
这次搜索将找到形如foam和roams的单词。
注意:使用模糊查询将自动得到增量因子(boost factor)为0.2的搜索结果.

邻近搜索(Proximity Searches)
Lucene还支持查找相隔一定距离的单词。邻近搜索是在短语最后加上符号"~"。例如在文档

中搜索相隔10个单词的"apache"和"jakarta",这样写: "jakarta apache"~10

Boosting a Term
Lucene provides the relevance level of matching documents based on the terms

found. To boost a term use the caret, "^", symbol with a boost factor (a

number) at the end of the term you are searching. The higher the boost factor,

the more relevant the term will be.
Lucene可以设置在搜索时匹配项的相似度。在项的最后加上符号"^"紧接一个数字(增量值

),表示搜索时的相似度。增量值越高,搜索到的项相关度越好。
Boosting allows you to control the relevance of a document by boosting its

term. For example, if you are searching for jakarta apache and you want the

term "jakarta" to be more relevant boost it using the ^ symbol along with the

boost factor next to the term. You would type:
通过增量一个项可以控制搜索文档时的相关度。例如如果您要搜索jakarta apache,同时

您想让"jakarta"的相关度更加好,那么在其后加上"^"符号和增量值,也就是您输入:
jakarta^4 apache
This will make documents with the term jakarta appear more relevant. You can

also boost Phrase Terms as in the example:
这将使得生成的doucment尽可能与jakarta相关度高。您也可以增量短语,象以下这个例子

一样:
"jakarta apache"^4 "jakarta lucene"

By default, the boost factor is 1. Although, the boost factor must be positive,

it can be less than 1 (i.e. .2)
默认情况下,增量值是1。增量值也可以小于1(例如0.2),但必须是有效的。

布尔操作符
布尔操作符可将项通过逻辑操作连接起来。Lucene支持AND, "+", OR, NOT 和 "-"这些操

作符。(注意:布尔操作符必须全部大写)

OR
OR操作符是默认的连接操作符。这意味着如果两个项之间没有布尔操作符,就是使用OR操

作符。OR操作符连接两个项,意味着查找含有任意项的文档。这与集合并运算相同。符号

||可以代替符号OR。
搜索含有"jakarta apache" 或者 "jakarta"的文档,可以使用这样的查询:
"jakarta apache" jakarta 或者 "jakarta apache" OR jakarta

AND
AND操作符匹配的是两项同时出现的文档。这个与集合交操作相等。符号&&可以代替符号

AND。
搜索同时含有"jakarta apache" 与 "jakarta lucene"的文档,使用查询:
"jakarta apache" AND "jakarta lucene"

+
"+"操作符或者称为存在操作符,要求符号"+"后的项必须在文档相应的域中存在。
搜索必须含有"jakarta",可能含有"lucene"的文档,使用查询:
+jakarta apache

NOT
NOT操作符排除那些含有NOT符号后面项的文档。这和集合的差运算相同。符号!可以代替

符号NOT。
搜索含有"jakarta apache",但是不含有"jakarta lucene"的文档,使用查询:
"jakarta apache" NOT "jakarta lucene"
注意:NOT操作符不能单独与项使用构成查询。例如,以下的查询查不到任何结果:
NOT "jakarta apache"

-
"-"操作符或者禁止操作符排除含有"-"后面的相似项的文档。
搜索含有"jakarta apache",但不是"jakarta lucene",使用查询:
"jakarta apache" -"jakarta lucene"

分组(Grouping)
Lucene支持使用圆括号来组合字句形成子查询。这对于想控制查询布尔逻辑的人十分有用


搜索含有"jakarta"或者"apache",同时含有"website"的文档,使用查询:
(jakarta OR apache) AND website
这样就消除了歧义,保证website必须存在,jakarta和apache中之一也存在。
转义特殊字符(Escaping Special Characters)
Lucene支持转义特殊字符,因为特殊字符是查询语法用到的。现在,特殊字符包括
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \
转义特殊字符只需在字符前加上符号\,例如搜索(1+1):2,使用查询
\(1\+1\)\:2  
(李宇翻译,来自Lucene的帮助文档)上面这段看了之后很有帮助,解除了使用中的不少

疑惑,谢谢翻译者,同时应该看到,有的时候详细查看使用帮助文档是非常有用的。
------------------------------------------------------------------------------
索引文件格式

本文定义了Lucene(版本1.3)用到的索引文件的格式。
Jakarta Lucene是用Java写成的,同时有很多团体正在默默的用其他的程序语言来改写它

。如果这些新的版本想和Jakarta Lucene兼容,就需要一个与具体语言无关的Lucene索引

文件格式。本文正是试图提供一个完整的与语言无关的Jakarta Lucene 1.3索引文件格式

的规格定义。
随着Lucene不断发展,本文也应该更新。不同语言写成的Lucene实现版本应当尽力遵守文

件格式,也必须产生本文的新版本。
本文同时提供兼容性批注,描述文件格式上与前一版本不同的地方。

定义
Lucene中最基础的概念是索引(index),文档(document),域(field)和项(term)


索引包含了一个文档的序列。
· 文档是一些域的序列。
· 域是一些项的序列。
· 项就是一个字串。
存在于不同域中的同一个字串被认为是不同的项。因此项实际是用一对字串表示的,第一

个字串是域名,第二个是域中的字串。

倒排索引
为了使得基于项的搜索更有效率,索引中项是静态存储的。Lucene的索引属于索引方式中

的倒排索引,因为对于一个项这种索引可以列出包含它的文档。这刚好是文档与项自然联

系的倒置。

域的类型
Lucene中,域的文本可能以逐字的非倒排的方式存储在索引中。而倒排过的域称为被索引

过了。域也可能同时被存储和被索引。
域的文本可能被分解许多项目而被索引,或者就被用作一个项目而被索引。大多数的域是

被分解过的,但是有些时候某些标识符域被当做一个项目索引是很有用的。

段(Segment)
Lucene索引可能由多个子索引组成,这些子索引成为段。每一段都是完整独立的索引,能

被搜索。索引是这样作成的:
1. 为新加入的文档创建新段。
2. 合并已经存在的段。
搜索时需要涉及到多个段和/或者多个索引,每一个索引又可能由一些段组成。

文档号(Document Number)
内部的来说,Lucene用一个整形(interger)的文档号来指示文档。第一个被加入到索引

中的文档就是0号,顺序加入的文档将得到一个由前一个号码递增而来的号码。
注意文档号是可能改变的,所以在Lucene外部存储这些号码时必须小心。特别的,号码的

改变的情况如下:
· 只有段内的号码是相同的,不同段之间不同,因而在一个比段广泛的上下文环境中使用

这些号码时,就必须改变它们。标准的技术是根据每一段号码多少为每一段分配一个段号

。将段内文档号转换到段外时,加上段号。将某段外的文档号转换到段内时,根据每段中

可能的转换后号码范围来判断文档属于那一段,并减调这一段的段号。例如有两个含5个文

档的段合并,那么第一段的段号就是0,第二段段号5。第二段中的第三个文档,在段外的

号码就是8。
· 文档删除后,连续的号码就出现了间断。这可以通过合并索引来解决,段合并时删除的

文档相应也删掉了,新合并而成的段并没有号码间断。

绪论
索引段维护着以下的信息:
· 域集合。包含了索引中用到的所有的域。
· 域值存储表。每一个文档都含有一个“属性-值”对的列表,属性即为域名。这个列表

用来存储文档的一些附加信息,如标题,url或者访问数据库的一个ID。在搜索时存储域的

集合可以被返回。这个表以文档号标识。
· 项字典。这个字典含有所有文档的所有域中使用过的的项,同时含有使用过它的文档的

文档号,以及指向使用频数信息和位置信息的指针。
· 项频数信息。对于项字典中的每个项,这些信息包含含有这个项的文档的总数,以及每

个文档中使用的次数。
· 项位置信息。对于项字典中的每个项,都存有在每个文档中出现的各个位置。
· Normalization factors. For each field in each document, a value is stored

that is multiplied into the score for hits on that field. 标准化因子。对于文档

中的每一个域,存有一个值,用来以后乘以这个这个域的命中数(hits)。
· 被删除的文档信息。这是一个可选文件,用来表明那些文档已经删除了。
接下来的各部分部分详细描述这些信息。

文件的命名(File Naming)
同属于一个段的文件拥有相同的文件名,不同的扩展名。扩展名由以下讨论的各种文件格

式确定。
一般来说,一个索引存放一个目录,其所有段都存放在这个目录里,尽管我们不要求您这

样做。

基本数据类型(Primitive Types)

Byte
最基本的数据类型就是字节(byte,8位)。文件就是按字节顺序访问的。其它的一些数据

类型也定义为字节的序列,文件的格式具有字节意义上的独立性。

UInt32
32位无符号整数,由四个字节组成,高位优先。

UInt32 --> <Byte>4
Uint64
64位无符号整数,由八字节组成,高位优先。

UInt64 --> <Byte>8
VInt
可变长的正整数类型,每字节的最高位表明还剩多少字节。每字节的低七位表明整数的值

。因此单字节的值从0到127,两字节值从128到16,383,等等。

VInt 编码示例
Value
First byte
Second byte
Third byte

0
00000000
1
00000001
2
00000010
...
127
01111111
128
10000000
00000001
129
10000001
00000001
130
10000010
00000001
...
16,383
11111111
01111111
16,384
10000000
10000000
00000001
16,385
10000001
10000000
00000001
...

这种编码提供了一种在高效率解码时压缩数据的方法。

Chars
Lucene输出UNICODE字符序列,使用标准UTF-8编码。

String
Lucene输出由VINT和字符串组成的字串,VINT表示字串长,字符串紧接其后。
String --> VInt, Chars

索引包含的文件(Per-Index Files)
这部分介绍每个索引包含的文件。

Segments文件
索引中活动的段存储在Segments文件中。每个索引只能含有一个这样的文件,名

为"segments".这个文件依次列出每个段的名字和每个段的大小。
Segments --> SegCount, <SegName, SegSize>SegCount
SegCount, SegSize --> UInt32
SegName --> String
SegName表示该segment的名字,同时作为索引其他文件的前缀。
SegSize是段索引中含有的文档数。

Lock文件
有一些文件用来表示另一个进程在使用索引。
· 如果存在"commit.lock"文件,表示有进程在写"segments"文件和删除无用的段索引文

件,或者表示有进程在读"segments"文件和打开某些段的文件。在一个进程在读

取"segments"文件段信息后,还没来得及打开所有该段的文件前,这个Lock文件可以防止

另一个进程删除这些文件。
· 如果存在"index.lock"文件,表示有进程在向索引中加入文档,或者是从索引中删除文

档。这个文件防止很多文件同时修改一个索引。

Deleteable文件
名为"deletetable"的文件包含了索引不再使用的文件的名字,这些文件可能并没有被实际

的删除。这种情况只存在与Win32平台下,因为Win32下文件仍打开时并不能删除。
Deleteable --> DelableCount, <DelableName>DelableCount
DelableCount --> UInt32
DelableName --> String

段包含的文件(Per-Segment Files)
剩下的文件是每段中包含的文件,因此由后缀来区分。
域(Field)
域集合信息(Field Info)
所有域名都存储在这个文件的域集合信息中,这个文件以后缀.fnm结尾。
FieldInfos (.fnm) --> FieldsCount, <FieldName, FieldBits>FieldsCount
FieldsCount --> VInt
FieldName --> String
FieldBits --> Byte
目前情况下,FieldBits只有使用低位,对于已索引的域值为1,对未索引的域值为0。
文件中的域根据它们的次序编号。因此域0是文件中的第一个域,域1是接下来的,等等。

这个和文档号的编号方式相同。
域值存储表(Stored Fields)
域值存储表使用两个文件表示:

1. 域索引(.fdx文件)。
如下,对于每个文档这个文件包含指向域值的指针:
FieldIndex (.fdx) --> <FieldValuesPosition>SegSize
FieldValuesPosition --> Uint64
FieldValuesPosition指示的是某一文档的某域的域值在域值文件中的位置。因为域值文件

含有定长的数据信息,因而很容易随机访问。在域值文件中,文档n的域值信息就存在n*8

位置处(The position of document n's field data is the Uint64 at n*8 in this

file.)。

2. 域值(.fdt文件)。
如下,每个文档的域值信息包含:
FieldData (.fdt) --> <DocFieldData>SegSize
DocFieldData --> FieldCount, <FieldNum, Bits, Value>FieldCount
FieldCount --> VInt
FieldNum --> VInt
Bits --> Byte
Value --> String
目前情况下,Bits只有低位被使用,值为1表示域名被分解过,值为0表示未分解过。

项字典(Term Dictionary)
项字典用以下两个文件表示:
1. 项信息(.tis文件)。
TermInfoFile (.tis)--> TermCount, TermInfos
TermCount --> UInt32
TermInfos --> <TermInfo>TermCount
TermInfo --> <Term, DocFreq, FreqDelta, ProxDelta>
Term --> <PrefixLength, Suffix, FieldNum>
Suffix --> String
PrefixLength, DocFreq, FreqDelta, ProxDelta
--> VInt
项信息按项排序。项信息排序时先按项所属的域的文字顺序排序,然后按照项的字串的文

字顺序排序。
项的字前缀往往是共同的,与字的后缀组成字。PrefixLength变量就是表示与前一项相同

的前缀的字数。因此,如果前一个项的字是"bone",后一个是"boy"的话,PrefixLength值

为2,Suffix值为"y"。

FieldNum指明了项属于的域号,而域名存储在.fdt文件中。
DocFreg表示的是含有该项的文档的数量。
FreqDelta指明了项所属TermFreq变量在.frq文件中的位置。详细的说,就是指相对于前一

个项的数据的位置偏移量(或者是0,表示文件中第一个项)。
ProxDelta指明了项所属的TermPosition变量在.prx文件中的位置。详细的说,就是指相对

于前一个项的数据的位置偏移量(或者是0,表示文件中第一个项)。

2. 项信息索引(.tii文件)。
每个项信息索引文件包含.tis文件中的128个条目,依照条目在.tis文件中的顺序。这样设

计是为了一次将索引信息读入内存能,然后使用它来随机的访问.tis文件。
这个文件的结构和.tis文件非常类似,只在每个条目记录上增加了一个变量IndexDelta。
TermInfoIndex (.tii)--> IndexTermCount, TermIndices
IndexTermCount --> UInt32
TermIndices --> <TermInfo, IndexDelta>IndexTermCount
IndexDelta --> VInt
IndexDelta表示该项的TermInfo变量值在.tis文件中的位置。详细的讲,就是指相对于前

一个条目的偏移量(或者是0,对于文件中第一个项)。

项频数(Frequencies)
.frq文件包含每一项的文档的列表,还有该项在对应文档中出现的频数。
FreqFile (.frq) --> <TermFreqs>TermCount
TermFreqs --> <TermFreq>DocFreq
TermFreq --> DocDelta, Freq?
DocDelta,Freq --> VInt
TermFreqs序列按照项来排序(依据于.tis文件中的项,即项是隐含存在的)。
TermFreq元组按照文档号升序排列。
DocDelta决定了文档号和频数。详细的说,DocDelta/2表示相对于前一文档号的偏移量(

或者是0,表示这是TermFreqs里面的第一项)。当DocDelta是奇数时表示在该文档中频数

为1,当DocDelta是偶数时,另一个VInt(Freq)就表示在该文档中出现的频数。
例如,假设某一项在文档7中出现一次,在文档11中出现了3次,在TermFreqs中就存在如下

的VInts序列: 15, 22, 3

项位置(Position)
.prx文件包含了某文档中某项出现的位置信息的列表。
ProxFile (.prx) --> <TermPositions>TermCount
TermPositions --> <Positions>DocFreq
Positions --> <PositionDelta>Freq
PositionDelta --> VInt
TermPositions按照项来排序(依据于.tis文件中的项,即项是隐含存在的)。
Positions元组按照文档号升序排列。
PositionDelta是相对于前一个出现位置的偏移位置(或者为0,表示这是第一次在这个文

档中出现)。
例如,假设某一项在某文档第4项出现,在另一个文档中第5项和第9项出现,将存在如下的

VInt序列: 4, 5, 4

标准化因子(Normalization Factor)
.nrm文件包含了每个文档的标准化因子,标准化因子用来以后乘以这个这个域的命中数。
Norms (.nrm) --> <Byte>SegSize
每个字节记录一个浮点数。位0-2包含了3位的尾数部分,位3-8包含了5位的指数部分。
按如下规则可将这些字节转换为IEEE标准单精度浮点数:
1. 如果该字节是0,就是浮点0;
2. 否则,设置新浮点数的标志位为0;
3. 将字节中的指数加上48后作为新的浮点数的指数;
4. 将字节中的尾数映射到新浮点数尾数的高3位;并且
5. 设置新浮点数尾数的低21位为0。

被删除的文档(Deleted Document)
.del文件是可选的,只有在某段中存在删除操作后才存在:
Deletions (.del) --> ByteCount,BitCount,Bits
ByteSize,BitCount --> Uint32
Bits --> <Byte>ByteCount
ByteCount表示的是Bits列表中Byte的数量。典型的,它等于(SegSize/8)+1。
BitCount表示Bits列表中多少个已经被设置过了。
Bits列表包含了一些位(bit),顺序表示一个文档。当对应于文档号的位被设置了,就标

志着这个文档已经被删除了。位的顺序是从低到高。因此,如果Bits包含两个字节,0x00

和0x02,那么表示文档9已经删除了。

局限性(Limitations)
在以上的文件格式中,好几处都有限制项和文档的最大个数为32位数的极限,即接近于40

亿。今天看来,这不会造成问题,但是,长远的看,可能造成问题。因此,这些极限应该

或者换为UInt64类型的值,或者更好的,换为VInt类型的值(VInt值没有上限)。
有两处地方的代码要求必须是定长的值,他们是:
1. FieldValuesPosition变量(存储于域索引文件中,.fdx文件)。它已经是一个UInt64

型,所以不会有问题。
2. TermCount变量(存储于项信息文件中,.tis文件)。这是最后输出到文件中的,但是

最先被读取,因此是存储于文件的最前端 。索引代码先在这里写入一个0值,然后在其他

文件输出完毕后覆盖这个值。所以无论它存储在什么地方,它都必须是一个定长的值,它

应该被变成UInt64型。
除此之外,所有的UInt值都可以换成VInt型以去掉限制
------------------------------------------------------------------------------

---------
下面是lucene组成结构中的类说明:
org.apache.Lucene.search/ 搜索入口
org.apache.Lucene.index/ 索引入口
org.apache.Lucene.analysis/ 语言分析器
org.apache.Lucene.queryParser/ 查询分析器
org.apache.Lucene.document/ 存储结构
org.apache.Lucene.store/  底层IO/存储结构
org.apache.Lucene.util/ 一些公用的数据结构

域存储字段规则
方法 切词 索引 存储 用途
Field.Text(String name, String value) 切分词索引并存储,比如:标题,内容字段
Field.Text(String name, Reader value)  切分词索引不存储,比如:META信息,
不用于返回显示,但需要进行检索内容
Field.Keyword(String name, String value)  不切分索引并存储,比如:日期字段
Field.UnIndexed(String name, String value)  不索引,只存储,比如:文件路径
Field.UnStored(String name, String value)  只全文索引,不存储

建立索引的例子:
public class IndexFiles {  
//使用方法:: IndexFiles [索引输出目录] [索引的文件列表] ...  
public static void main(String[] args) throws Exception {   
String indexPath = args[0];    IndexWriter writer;   
//用指定的语言分析器构造一个新的写索引器(第3个参数表示是否为追加索引)   

writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);   
for (int i=1; i<args.length; i++) {     
System.out.println("Indexing file " + args[i]);     
InputStream is = new FileInputStream(args[i]);     
//构造包含2个字段Field的Document对象     
//一个是路径path字段,不索引,只存储     
//一个是内容body字段,进行全文索引,并存储     
Document doc = new Document();     
doc.add(Field.UnIndexed("path", args[i]));     
doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));     
//将文档写入索引    
 writer.addDocument(doc);     
is.close();    };   
//关闭写索引器   
writer.close();  }
}
索引过程中可以看到:

语言分析器提供了抽象的接口,因此语言分析(Analyser)是可以定制的,虽然lucene缺省

提供了2个比较通用的分析器SimpleAnalyser和StandardAnalyser,这2个分析器缺省都不

支持中文,所以要加入对中文语言的切分规则,需要修改这2个分析器。
Lucene并没有规定数据源的格式,而只提供了一个通用的结构(Document对象)来接受索

引的输入,因此输入的数据源可以是:数据库,WORD文档,PDF文档,HTML文档……只要能

够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。
对于大批量的数据索引,还可以通过调整IndexerWrite的文件合并频率属性(mergeFactor

)来提高批量索引的效率。
检索过程和结果显示:

搜索结果返回的是Hits对象,可以通过它再访问Document==>Field中的内容。

假设根据body字段进行全文检索,可以将查询结果的path字段和相应查询的匹配度(score)

打印出来,

public class Search {  
public static void main(String[] args) throws Exception {   
String indexPath = args[0], queryString = args[1];   
//指向索引目录的搜索器   
Searcher searcher = new IndexSearcher(indexPath);   
//查询解析器:使用和索引同样的语言分析器   
Query query = QueryParser.parse(queryString, "body",                           

   new SimpleAnalyzer());   
//搜索结果使用Hits存储   
Hits hits = searcher.search(query);   
//通过hits可以访问到相应字段的数据和查询的匹配度   
for (int i=0; i<hits.length(); i++) {     
System.out.println(hits.doc(i).get("path") + "; Score: " +                     

    hits.score(i));    };  }
}
添加修改删除指定记录(Document)

Lucene提供了索引的扩展机制,因此索引的动态扩展应该是没有问题的,而指定记录的修

改也似乎只能通过记录的删除,然后重新加入实现。如何删除指定的记录呢?删除的方法

也很简单,只是需要在索引时根据数据源中的记录ID专门另建索引,然后利用

IndexReader.delete(Termterm)方法通过这个记录ID删除相应的Document。

根据某个字段值的排序功能
根据某个字段值的排序功能

lucene缺省是按照自己的相关度算法(score)进行结果排序的,但能够根据其他字段进行

结果排序是一个在LUCENE的开发邮件列表中经常提到的问题,很多原先基于数据库应用都

需要除了基于匹配度(score)以外的排序功能。而从全文检索的原理我们可以了解到,任

何不基于索引的搜索过程效率都会导致效率非常的低,如果基于其他字段的排序需要在搜

索过程中访问存储字段,速度回大大降低,因此非常是不可取的。

但这里也有一个折中的解决方法:在搜索过程中能够影响排序结果的只有索引中已经存储

的docID和score这2个参数,所以,基于score以外的排序,其实可以通过将数据源预先排

好序,然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行

排序和在搜索过程中访问不在索引中的某个字段值。

这里需要修改的是IndexSearcher中的HitCollector过程:

... scorer.score(new HitCollector() { 
private float minScore = 0.0f; 
public final void collect(int doc, float score) {  
if (score > 0.0f &&     // ignore zeroed buckets      

(bits==null || bits.get(doc))) {   // skip docs not in bits    

totalHits[0]++;     if (score >= minScore) {              /* 原先:Lucene将

docID和相应的匹配度score例入结果命中列表中:        * hq.put(new ScoreDoc

(doc, score));   // update hit queue               * 如果用doc 或 1/doc 代替

score,就实现了根据docID顺排或逆排               * 假设数据源索引时已经按照某个

字段排好了序,而结果根据docID排序也就实现了               * 针对某个字段的排序

,甚至可以实现更复杂的score和docID的拟合。               */             

hq.put(new ScoreDoc(doc, (float) 1/doc ));       
if (hq.size() > nDocs) {    // if hit queue overfull  

hq.pop();     // remove lowest in hit queue  

minScore = ((ScoreDoc)hq.top()).score; // reset minScore       }     } 

  } }      }, reader.maxDoc());

Lucene面向全文检索的优化在于首次索引检索后,并不把所有的记录(Document)具体内

容读取出来,而起只将所有结果中匹配度最高的头100条结果(TopDocs)的ID放到结果集

缓存中并返回,这里可以比较一下数据库检索:如果是一个10,000条的数据库检索结果集

,数据库是一定要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索

匹配总数很多,Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是

用不到这么多的结果的,头100条已经可以满足90%以上的检索需求。

如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次

的搜索缓存数大1倍的缓存,并再重新向后抓取。所以如果构造一个Searcher去查1-120条

结果,Searcher其实是进行了2次搜索过程:头100条取完后,缓存结果用完,Searcher重

新检索再构造一个200条的结果缓存,依此类推,400条缓存,800条缓存。由于每次

Searcher对象消失后,这些缓存也访问那不到了,你有可能想将结果记录缓存下来,缓存

数尽量保证在100以下以充分利用首次的结果缓存,不让Lucene浪费多次检索,而且可以分

级进行结果缓存。

Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和

数据库应用需要将搜索的结果全部返回不同之处。

posted @ 2006-11-08 14:58 Lansing 阅读(1350) | 评论 (0)编辑 收藏

Eclipse及其插件介绍和下载- -

TagEclipse    插件                                          

0.Eclipse下载
EMF,GEF - Graphical Editor Framework,UML2,VE - Visual Editor都在这里下载
http://www.eclipse.org/downloads/index.php
 
0.5.lomboz J2EE插件,开发JSP,EJB
http://forge.objectweb.org/projects/lomboz
1.MyEclipse J2EE开发插件,支持SERVLET/JSP/EJB/数据库操纵等
http://www.myeclipseide.com
 
2.Properties Editor  编辑java的属性文件,并可以自动存盘为Unicode格式
http://propedit.sourceforge.jp/index_en.html
 
3.Colorer Take  为上百种类型的文件按语法着色
http://colorer.sourceforge.net/
 
4.XMLBuddy 编辑xml文件
http://www.xmlbuddy.com
 
5.Code Folding  加入多种代码折叠功能(比eclipse自带的更多)
http://www.coffee-bytes.com/servlet/PlatformSupport
 
6.Easy Explorer  从eclipse中访问选定文件、目录所在的文件夹
http://easystruts.sourceforge.net/
 
7.Fat Jar 打包插件,可以方便的完成各种打包任务,可以包含外部的包等
http://fjep.sourceforge.net/
 
8.RegEx Test 测试正则表达式
http://brosinski.com/stephan/archives/000028.php
 
9.JasperAssistant 报表插件(强,要钱的)
http://www.jasperassistant.com/
 
10.Jigloo GUI Builder JAVA的GUI编辑插件
http://cloudgarden.com/jigloo/
 
11.Profiler 性能跟踪、测量工具,能跟踪、测量BS程序
http://sourceforge.net/projects/eclipsecolorer/
 
12.AdvanQas 提供对if/else等条件语句的提示和快捷帮助(自动更改结构等)
http://eclipsecolorer.sourceforge.net/advanqas/index.html
 
13.Log4E Log4j插件,提供各种和Log4j相关的任务,如为方法、类添加一个logger等
http://log4e.jayefem.de/index.php/Main_Page
 
14.VSSPlugin VSS插件
http://sourceforge.net/projects/vssplugin
 
15.Implementors 提供跳转到一个方法的实现类,而不是接中的功能(实用!)
http://eclipse-tools.sourceforge.net/implementors/
 
16.Call Hierarchy 显示一个方法的调用层次(被哪些方法调,调了哪些方法)
http://eclipse-tools.sourceforge.net/call-hierarchy/index.html
 
17.EclipseTidy 检查和格式化HTML/XML文件
http://eclipsetidy.sourceforge.net/
 
18.Checkclipse 检查代码的风格、写法是否符合规范
http://www.mvmsoft.de/content/plugins/checkclipse/checkclipse.htm
 
19.Hibernate Synchronizer Hibernate插件,自动映射等
http://www.binamics.com/hibernatesync/
 
20.VeloEclipse  Velocity插件
http://propsorter.sourceforge.net/
 
21.EditorList 方便的列出所有打开的Editor
http://editorlist.sourceforge.net/
 
22.MemoryManager 内存占用率的监视
http://cloudgarden.com/memorymanager/
 
23.swt-designer java的GUI插件
http://www.swt-designer.com/
 
24.TomcatPlugin 支持Tomcat插件
http://www.sysdeo.com/eclipse/tomcatPlugin.html
 
25.XML Viewer
http://tabaquismo.freehosting.net/ignacio/eclipse/xmlview/index.html
 
26.quantum 数据库插件
http://quantum.sourceforge.net/
 
27.Dbedit 数据库插件
http://sourceforge.net/projects/dbedit
 
28.clay.core 可视化的数据库插件
http://www.azzurri.jp/en/software/index.jsp
http://www.azzurri.jp/eclipse/plugins
 
29.hiberclipse hibernate插件
http://hiberclipse.sourceforge.net
http://www.binamics.com/hibernatesync
 
30.struts-console Struts插件
http://www.jamesholmes.com/struts/console/
 
31.easystruts Struts插件
http://easystruts.sourceforge.net
 
32.veloedit Velocity插件
http://veloedit.sourceforge.net/
 
33.jalopy 代码整理插件
http://jalopy.sourceforge.net/
 
34.JDepend 包关系分析
http://andrei.gmxhome.de/jdepend4eclipse/links.html
 
35.Spring IDE Spring插件
http://springide-eclip.sourceforge.net/updatesite/
 
36.doclipse 可以产生xdoclet 的代码提示
http://beust.com/doclipse/

posted @ 2006-09-21 19:17 Lansing 阅读(301) | 评论 (0)编辑 收藏

Struts常见异常信息和解决方法

以下所说的struts-config.xml和ApplicationResources.properties等文件名是缺省时使用的,如果你使用了多模块,或指定了不同的资源文件名称,这些名字要做相应的修改。

1、“No bean found under attribute key XXX”
在struts-config.xml里定义了一个ActionForm,但type属性指定的类不存在,type属性的值应该是Form类的全名。或者是,在Action的定义中,name或attribute属性指定的ActionForm不存在。

2、“Cannot find bean XXX in any scope”
在Action里一般会request.setAttribute()一些对象,然后在转向的jsp文件里(用tag或request.getAttribute()方法)得到这些对象并显示出来。这个异常是说jsp要得到一个对象,但前面的Action里并没有将对象设置到request(也可以是session、servletContext)里。
可能是名字错了,请检查jsp里的tag的一般是name属性,或getAttribute()方法的参数值;或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。
还有另外一个可能,纯粹是jsp文件的问题,例如<logic:iterate>会指定一个id值,然后在循环里<bean:write>使用这个值作为name的值,如果这两个值不同,也会出现此异常。(都是一个道理,request里没有对应的对象。)

3、“Missing message for key "XXX"”
缺少所需的资源,检查ApplicationResources.properties文件里是否有jsp文件里需要的资源,例如:

< bean:message key = " msg.name.prompt " />

这行代码会找msg.name.prompt资源,如果AppliationResources.properties里没有这个资源就会出现本异常。在使用多模块时,要注意在模块的struts-config-xxx.xml里指定要使用的资源文件名称,否则当然什么资源也找不到,这也是一个很容易犯的错误。

4、“No getter method for property XXX of bean teacher”
这条异常信息说得很明白,jsp里要取一个bean的属性出来,但这个bean并没有这个属性。你应该检查jsp中某个标签的property属性的值。例如下面代码中的cade应该改为code才对:

< bean:write name = " teacher "  property = " cade "  filter = " true " />

5、“Cannot find ActionMappings or ActionFormBeans collection”
待解决。

6、“Cannot retrieve mapping for action XXX”
在.jsp的<form>标签里指定action='/XXX',但这个Action并未在struts-config.xml里设置过。

7、HTTP Status 404 - /xxx/xxx.jsp
Forward的path属性指向的jsp页面不存在,请检查路径和模块,对于同一模块中的Action转向,path中不应包含模块名;模块间转向,记住使用contextRelative="true"。

8、没有任何异常信息,显示空白页面
可能是Action里使用的forward与struts-config.xml里定义的forward名称不匹配。

9、“The element type "XXX" must be terminated by the matching end-tag "XXX".”
这个是struts-config.xml文件的格式错误,仔细检查它是否是良构的xml文件,关于xml文件的格式这里就不赘述了。

10、“Servlet.init() for servlet action threw exception”
一般出现这种异常在后面会显示一个关于ActionServlet的异常堆栈信息,其中指出了异常具体出现在代码的哪一行。我曾经遇到的一次提示如下:

java.lang.NullPointerException
    at org.apache.struts.action.ActionServlet.parseModuleConfigFile(ActionServlet.java:
1003 )
    at org.apache.struts.action.ActionServlet.initModuleConfig(ActionServlet.java:
955 )


为解决问题,先下载struts的源码包,然后在ActionServlet.java的第1003行插入断点,并对各变量进行监视。很丢人,我竟然把struts-config.xml文件弄丢了,因此出现了上面的异常,应该是和CVS同步时不小心删除的。

11、“Resources not defined for Validator”
这个是利用Validator插件做验证时可能出现的异常,这时你要检查validation.xml文件,看里面使用的资源是否确实有定义,form的名称是否正确,等等。

posted @ 2006-09-19 11:34 Lansing 阅读(324) | 评论 (0)编辑 收藏
     摘要: PowerDesigner 设计数据库              本文档不讲述如何使用 PowerDesigner ,而是讲述如何将 PowerDesigner...  阅读全文
posted @ 2006-09-13 14:53 Lansing 阅读(660) | 评论 (0)编辑 收藏
Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显示内容。

  一、定义配置文件

  其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:

  1.配置根Logger,其语法为:

  log4j.rootLogger = [ level ] , appenderName, appenderName, …

  其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。 appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。

  2.配置日志信息输出目的地Appender,其语法为:

  log4j.appender.appenderName = fully.qualified.name.of.appender.class
  log4j.appender.appenderName.option1 = value1
  …
  log4j.appender.appenderName.option = valueN

  其中,Log4j提供的appender有以下几种:
  org.apache.log4j.ConsoleAppender(控制台),
  org.apache.log4j.FileAppender(文件),
  org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
  org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
  org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

  3.配置日志信息的格式(布局),其语法为:

  log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
  log4j.appender.appenderName.layout.option1 = value1
  …
  log4j.appender.appenderName.layout.option = valueN

  其中,Log4j提供的layout有以下几种:
  org.apache.log4j.HTMLLayout(以HTML表格形式布局),
  org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
  org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
  org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

  Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: %m 输出代码中指定的消息

  %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
  %r 输出自应用启动到输出该log信息耗费的毫秒数
  %c 输出所属的类目,通常就是所在类的全名
  %t 输出产生该日志事件的线程名
  %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
  %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
  %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)

  二、在代码中使用Log4j

  1.得到记录器

  使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:

  public static Logger getLogger( String name)

  通过指定的名字获得记录器,如果必要的话,则为这个名字创建一个新的记录器。Name一般取本类的名字,比如:

  static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () )

  2.读取配置文件

  当获得了日志记录器之后,第二步将配置Log4j环境,其语法为:

  BasicConfigurator.configure (): 自动快速地使用缺省Log4j环境。
  PropertyConfigurator.configure ( String configFilename) :读取使用Java的特性文件编写的配置文件。
  DOMConfigurator.configure ( String filename ) :读取XML形式的配置文件。

  3.插入记录信息(格式化日志信息)

  当上两个必要步骤执行完毕,您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:

  Logger.debug ( Object message ) ;
  Logger.info ( Object message ) ;
  Logger.warn ( Object message ) ;
  Logger.error ( Object message ) ;

posted @ 2006-08-22 14:28 Lansing 阅读(342) | 评论 (0)编辑 收藏
速度瓶颈问题的提出

  在企业级的Java应用中,访问数据库是一个必备的环节。数据库作为数据资源的集散地,往往位于企业级软件体系的后方,供前方的应用程序访问。在Java技术的体系中,应用程序是通过JDBC(Java Database Connectivity)接口来访问数据库的,JDBC支持"建立连接、SQL语句查询、处理结果"等基本功能。在应用JDBC接口访问数据库的过程中,只要根据规范来操作,这些功能的实现不会出差错。但是,有些时候进行数据查询的效率着实让开发人员懊恼不已,明明根据规范编写的程序,却得不到预期的运行效果,造成了整个软件的执行效率不高。

  起初,我们把问题归结于Java字节码加载和执行速度的缓慢,紧接着硬件的功能普遍得到了增强,证明这样的想法些许是错误的,还没有抓到真正的根本原因。本文将逐步解剖JDBC访问数据库的机制,深层分析造成这种速度瓶颈问题的原因,并提出在现有的Java技术框架下解决这个速度瓶颈问题的思路和方法。

  JDBC访问数据库的机制

                
图1                                                图2

  图1和图2描述了Java应用程序通过JDBC接口访问数据库的4种驱动模式,也就是底层实现JDBC接口的模式。对于这些模式,我们逐一介绍:

  模式4:图1左边的分支称为模式4,它一般是数据库厂商才能实现的纯Java的基于本地协议的驱动,直接调用DBMS(数据库管理系统)使用的网络协议,对于企业内部互联网来说,是一个实用的解决方案。

  模式3:图1右边的分支称为模式3,它同样是一个纯Java驱动,不同于模式4的是基于网络协议。它的机制是将JDBC调用转换为中间网络协议,然后转换为DBMS协议。中间网络协议层起到一个读取数据库的中间件的作用,能够连接许多类型的数据库,因而是最灵活的JDBC模式。这种模式的产品比较适用于企业内部互联网,如若支持国际互联网,还需添加对安全、穿过防火墙访问等的支持。

  模式1:图2左边的分支称为模式1,即通常由Sun公司提供的JDBC-ODBC桥接器。它提供了经由一种或多种ODBC驱动进行访问的JDBC接口,而ODBC驱动,在很多情况下也即数据库的客户端,必须加载到客户机。因而,它适用于下载和自动安装Java程序不重要、实验用途或者没有其它JDBC驱动可用的情况下。

  模式2:图2右边的分支成为模式2,类似于JDBC-ODBC桥接器,需要加载到客户机,却是一个部分用Java实现的驱动接口。它将JDBC调用转换为对数据库(Oracle、Sybase、Informix、DB2等)客户端接口的调用。

  不同模式的JDBC接口的选择

  以上阐述的JDBC接口的模式不同,让我们可以把JDBC接口按照实现的模式分为四类。有些同仁可能有这样的体会,选择不同的JDBC接口会有不同的访问速度,为何会出现这样的情况?这个问题的答案是,不同的应用需要不同模式的JDBC接口,因而我们在面对一个应用时,要慎重选择JDBC接口。

  通常的DBMS都支持微软提出的ODBC规范,因而模式1可当作您在设计和实现软件时的选择,它易于配置的特性能够让你把选择JDBC等烦恼的问题暂且抛在一边,让自己的Java程序能够及早地正常工作起来。

  一般说来,商业DBMS的提供者往往会为自己的数据库提供一个JDBC接口,应用的是模式4。这种模式的优势在于和数据库本身结合比较紧密,而且是纯Java的实现,在企业级的软件应用中,应该是首选。例如,对于Oracle数据库来说,有Oracle、SilverStream、DataDirect等公司提供这种类型的驱动,其性能往往被评价为最高效的、最可靠的驱动程序。但偶尔也有比较麻烦的情况,例如微软就不会提供MS SQL的JDBC接口,这时就需要到Sun的网站(http://industry.java.sun.com/products/jdbc/drivers)查找相关的模式4驱动,上面提到的DataDirect公司(http://www.datadirect-technologies.com/jdbc/jdbc.asp)就提供了支持MS SQL的模式4驱动,只是你需要支付750$购买这个JDBC驱动。

  同样是纯Java实现的模式3,与模式4相比,优势在于对多种数据库的支持,体现了其灵活性。在大型的企业级的软件应用中,后台数据库往往不是一个,而且是由不同的厂商支持的。不过,模式3的JDBC驱动往往提供许多企业级的特征,例如SSL安全、支持分布式事务处理和集中管理等,因而会对你特殊的用途有很大的帮助。是否选用,还在于你对扩展应用是否有需求以及对多DBMS的支持。

  谈到这儿,我对模式3和模式4作一个总结:两者都是纯Java实现的驱动,因而不需要数据库厂商提供附加的软件,就可以运行在任何标准的Java平台,性能上比较高效、可靠。

  了解上述3种JDBC的实现模式之后,模式2就更容易阐释了,你可以理解它为前三者利弊平衡的妥协产物:

  1. 借鉴模式1利用客户机本地代码库,加速数据访问的执行,但却摒除ODBC标准,而是支持厂商自己指定的性能扩展
  2. 借鉴模式3利用多层结构,上层用Java实现,利于跨平台应用和支持多数据库,但下层却改为本地代码,加速执行速度
  3. 借鉴模式4和数据库结合紧密的优点,部分用Java实现,更是对数据库性能有很大的扩展

  这种开放和高性能的特征得到了业界的肯定,因而被主要的数据库厂商强烈推荐。尽管它需要你下载本地代码库到客户机,但相对于你访问数据库速度的提高,这些应该只是举手之劳了。下面对4种实现JDBC的模式选择,归纳一下选择的顺序(当然是指你有选择余地的时候,不存在的话向后推延):

编号 选择过程分析 选择顺序
1 实验性环境下,尽可能选择易于配置的驱动,利于Java程序的开发,后期可在对应用环境进行判断后,再对JDBC模式进行选择 1>2>3>4
2 小型企业级环境下,不需要对多数据库的支持,因而模式2和3的有些优点并不能体现出来,强烈推荐你选择模式4的JDBC驱动 4>2=3>1
3 大型企业级环境下,需要对多数据库的支持,模式2和3各有千秋,但是更多情况下是你会选择速度较快的模式2 2>3>4>1

  对于不同厂商提供的但应用相同模式的JDBC接口,理论上比较不出效率的高低,你只有通过一定的工具,例如Benchmark等,对它们进行比较才能更有利于你的选择。因为暂时不存在第三方提供的数据比较结果,所以这些问题需要你对上述内容有了透彻理解之后自行解决。

  Java程序中SQL语句格式的优化

  这个时候,你也许还在为找不到合适的JDBC驱动而一筹莫展,也许为自己在凌晨3点下载的JDBC驱动通过了测试而欣喜若狂,但是并不说明你对程序的优化工作已经无关紧要了。切记,对整个软件系统的优化,包括每个环节的优化,要不有可能你会前功尽弃。我在这儿不和大家讨论Java程序的算法,而是简单阐述一下选择SQL语句格式的必要和如何选择对自己有利的SQL语句格式。看下面两段程序片断:

  Code Fragment 1:

  String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE 'Colombian'";
  stmt.executeUpdate(updateString);

  Code Fragment 2:

  PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
  updateSales.setInt(1, 75);
  updateSales.setString(2, "Colombian");
  updateSales.executeUpdate();

  片断2和片断1的区别在于,后者使用了PreparedStatement对象,而前者是普通的Statement对象。PreparedStatement对象不仅包含了SQL语句,而且大多数情况下这个语句已经被预编译过,因而当其执行时,只需DBMS运行SQL语句,而不必先编译。当你需要执行Statement对象多次的时候,用PreparedStatement对象将会大大降低运行时间,当然也加快了访问数据库的速度。

  这种转换也给你带来很大的便利,不必重复SQL语句的句法,而只需更改其中变量的值,便可重新执行SQL语句。选择PreparedStatement对象与否,在于相同句法的SQL语句是否执行了多次,而且两次之间的差别仅仅是变量的不同。如果仅仅执行了一次的话,它应该和普通的Statement对象毫无差异,体现不出它预编译的优越性。

  软件模型中对数据库访问的设计模式的优化

  在我阅读J2EE蓝图和JDO草案的过程中,我发现了访问模式对数据库访问的影响,因而想在本文中阐述如何针对自己的软件需求选择合适的软件模式。

  J2EE蓝图的设计者在Java Pet Store示例应用中使用了MVC(Model-View-Controller)体系,给许多J2EE设计模式提供了背景。我要谈及的三种设计模式是:Data Access Object、Fast Lane Reader、Page-by-Page Iterator,它们为加快数据存取速度提供了一些可以在系统设计阶段值得我们借鉴的想法。

  Data Access Object

  将商业逻辑从数据存取逻辑中分离出来,把存取的资源改编,从而使资源可以容易和独立地转变。

  依赖于底层数据资源的特殊要素(例如数据库的供应商)的商业组件,常将商业逻辑和数据存取逻辑配合起来,只能使用特殊类型的资源,而使用不同类型的资源时,复用将会非常困难,因此,只能服务于有限的市场领域。DAO(Data Access Object)即是将数据存取逻辑从EJB中抽去出来抽象为一个独立的接口,EJB根据接口的操作执行商业逻辑,而接口针对使用的数据资源实现为DAO对象。

  在Java Pet Shop这个例子中,OrderEJB组件通过关联的OrderDAO类访问数据库,自身则关注于商业逻辑的实现。在调度阶段,将配置某一类(OrderDAOCS、OrderDAOOracle或OrderDAOSybase)为OrderDAO的实现,而OrderEJB无须任何更改。图3更能帮助你明白其中的道理:


图3 Data Access Object的设计模式

  此举增加了数据存取的弹性、资源的独立性和扩展性,但复杂度有相应的提高,其它附带的问题我们不在这儿讨论。

  Fast Lane Reader

  抛弃EJB,加速只读数据的存取。

  有些时候,高效地存取数据比获得最新的数据更重要。在Java Pet Store中,当一个用户浏览商店的目录时,屏幕与数据库内容吻合不是至关紧要的,相反,迅速显示和重新获得非常重要。FLR模式可以加速从资源中重新获得大型的列数据项的速度,它不用EJB,而是更直接地通过DAO来存取数据,从而消除EJB的经常开支(例如远程方法调用、事务管理和数据序列化等)。

  在Java Pet Store这个例子中,当一个用户浏览目录时,通过CatalogDAO(而不是CatalogEJB)从数据库加载数据项,而CatalogDAO是一个Fast Lane Reader的实例,使得读访问变得迅速,如图4:


图4 Fast Lane Reader设计模式

  与DAO模式不同的是,FLR是一个优化的模式,但不是要替代原有的访问机制,而是作为补充使其完备。当你频繁地只读大型的列数据和不必存取最新的数据时,使用FLR将是非常合适的。

  Page-by-Page Iterator

  为了高效地存取大型的远程数据列,一下子通过重新获得其元素为一个子列的Value Object(提高远程传输效率的设计模式,这儿不详尽描述)。

  分布式数据库的应用经常需要用户考虑一长列数据项,例如一个目录或一个搜索结果的集合。在这些情况下,立刻提供全列的数据经常不必要(用户并不是对所有的数据项感兴趣)或不可能(没有足够的空间)。此外,当重新获得一列数据项时,使用Entity Bean的代价将非常高昂,一个开销来自于使用远程探测器来收集Requested Bean,另外,更大的开销来自对每个Bean产生远程调用以从用户获得数据。

  通过Iterator,客户机对象能一下子重新获得一个子列或者页的Value Object,每一页都刚好满足客户机的需求,因此,程序使用较少的资源满足了客户机的立刻需求。

  在Java Pet Store这个例子中,JSP页面product.jsp任何时候只显示一个数据列的一部分,从ProductItemListTag(Page-by-Page Iterator)重新获得数据项,当客户机希望看到列中的别的数据项,product.jsp再次调用Iterator重新获得这些数据项,流程见图5:


图5 Page-by-Page Iterator设计模式

  以上设计模式的应用向我们表明,在某些特殊情况下,优化对数据库的访问模型,既可满足用户的需求又可提高访问数据库的效率。这给我们一个思路,就是:在你的硬件环境可能会产生瓶颈的情况下,可以通过对软件模型的优化来达到满足需求的目的。上述三种设计模式的应用情形为:

Data Access Object 需要将商业逻辑和数据存取逻辑分离;
在调度的时刻,需要支持选择数据源的类型;
使用的数据源的类型的变化,对商业对象或其它客户端完成数据存取没有影响。
Fast Lane Reader 面对大型的列数据,需要经常的只读访问;
访问最新的数据并不是至关紧要的事情。
Page-by-Page Iterator 存取大型的服务器端数据列;
任何时刻,用户只对列的一部分内容感兴趣;
整个列的数据不适合在客户端显示;
整个列的数据不适合在存储器中保存;
传输整个列的数据将耗费太多的时间。

  在显示商品目录的时候,我们选择了DAO和FLR的结合,因为它们两者的条件都得到了满足(需要分离商业逻辑和数据存取逻辑,经常的只读访问和对即时性不敏感),此时应用将会大大发挥它们的优点。而在进行内容检索的时候,我们会选择PPI,因为也许检索出了上千条的记录,但是用户没有兴趣立即阅读全部内容,而是一次十条地阅读,或者他在阅读完前十条记录后发觉自己的目的已经达到,接下来浏览别的网页了,都不必我们一次性地传输上千条记录给他,所以也是PPI的应用条件得到了满足,结果则是此模式的优点得到了发挥,又不影响全局的数据访问。

  在进行软件模型的设计时,整体的框架可以应用某些优秀的、通用的设计模式,这样既加快模型的建立速度,又能和其它系统集成。但是,碰到一些瓶颈问题的情况下,我们就需要对局部的设计模式做一些调整,以优化整个系统,上述三个模式就是对原有体系的补充,它们并没有对整体的框架做出巨大的改变,却突破了某些瓶颈(瓶颈往往是局部的)障碍,让我们的产品更好地服务于用户。

  将深入研究的问题

  开篇至今,我们主要探讨了软件层次上的解决问题,但是,必须肯定一点,如果你的硬件环境非常差(运行Java都有困难)或非常好(额外的存储空间、超快的运算速度和富裕的网络带宽),上述途径对你来说很难有大的帮助。前一种情况,我建议你升级硬件设备到软件厂商推荐的配置(强烈反对最小配置),以使应用服务器、数据库、Java等软件能够运行自如;后一种情况,我没什么话可说,花钱是解决这个问题最好的办法。

  本文并未谈及线程池和告诉缓冲这两个非常重要的概念,因为笔者认为,它们是针对局部时间高访问量的瓶颈问题的解决,不能理解为简单的速度瓶颈问题,所以我会在下一篇文章中分析这种特殊的情况和提出解决问题的办法。也许你对这一点更关心一些,认为自己的问题就出在这个地方,这是非常好的思考问题的方式,你已经抓住了问题的关键。但是,我还是建议你通读一下本文,让自己对速度瓶颈问题有更好的理解,并掌握在解决问题的过程中,分辨常态和暂态,从而选择不同的思路入手。其实,本文谈及的就是速度瓶颈问题的常态,而下一篇文章讨论的将会是暂态,希望你能够渐入佳境。

  JDO(Java Data Object)是需要我们关注的一个API,它定义了新的数据存取模型,直接借鉴了DAO设计模式。不同的数据源,有不同的数据存取技术,就有不同的API供开发人员使用。JDO正是为了解决这个问题而产生的,它实现了即插即用的数据存取的实现和持久信息(包括企业数据和本地存储的数据)以Java为中心的视图。因此,开发人员只关注创建那些实现商业逻辑的类和用它们来表现数据源的数据,而这些类和数据源之间的映射则由那些EIS领域的专家来完成。如果大家对JDO感兴趣的话,那么我会写第三篇文章把其详细介绍给大家,并给出示例应用。

posted @ 2006-08-22 09:32 Lansing 阅读(426) | 评论 (0)编辑 收藏
批量处理JDBC语句提高处理速度

作者:佚名    来自:未知

  有时候JDBC运行得不够快,这使得有些程序员使用数据库相关的存储过程。作为一个替代方案,可以试试使用Statement 的批量处理特性看看能否同时执行所有的SQL以提高速度。

  存储过程的最简单的形式就是包含一系列SQL语句的过程,将这些语句放在一起便于在同一个地方管理也可以提高速度。Statement 类可以包含一系列SQL语句,因此允许在同一个数据库事务执行所有的那些语句而不是执行对数据库的一系列调用。

  使用批量处理功能涉及下面的两个方法:
  · addBatch(String) 方法
  · executeBatch方法

  如果你正在使用Statement 那么addBatch 方法可以接受一个通常的SQL语句,或者如果你在使用PreparedStatement ,那么也可以什么都不向它增加。executeBatch 方法执行那些SQL语句并返回一个int值的数组,这个数组包含每个语句影响的数据的行数。如果将一个SELECT语句或者其他返回一个ResultSet的SQL语句放入批量处理中就会导致一个SQLException异常。

  关于java.sql.Statement 的简单范例可以是:

  Statement stmt = conn.createStatement();
  stmt.insert("DELETE FROM Users");
  stmt.insert("INSERT INTO Users VALUES("rod", 37, "circle")");
  stmt.insert("INSERT INTO Users VALUES("jane", 33, "triangle")");
  stmt.insert("INSERT INTO Users VALUES("freddy", 29, "square")");
  int[] counts = stmt.executeBatch();

  PreparedStatement 有些不同,它只能处理一部分SQL语法,但是可以有很多参数,因此重写上面的范例的一部分就可以得到下面的结果:

  // 注意这里没有DELETE语句
  PreparedStatement stmt = conn.prepareStatement("INSERT INTO Users VALUES(?,?,?)");

  User[ ] users = ...;
  for(int i=0; i<users.length; i++) {
   stmt.setInt(1, users[i].getName());
   stmt.setInt(2, users[i].getAge());
   stmt.setInt(3, users[i].getShape());
   stmt.addBatch( );
  }
  int[ ] counts = stmt.executeBatch();

  如果你不知道你的语句要运行多少次,那么这是一个很好的处理SQL代码的方法。在不使用批量处理的情况下,如果添加50个用户,那么性能就有影响,如果某个人写了一个脚本添加一万个用户,程序可能变得很糟糕。添加批处理功能就可以帮助提高性能,而且在后面的那种情况下代码的可读性也更好。

posted @ 2006-08-22 09:24 Lansing 阅读(413) | 评论 (0)编辑 收藏
<2006年8月>
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

欢迎探讨,努力学习Java哈

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

文章档案

Lansing's Download

Lansing's Link

我的博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜