飛艷小屋

          程序--人生--哲學(xué)___________________歡迎艷兒的加入

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            52 Posts :: 175 Stories :: 107 Comments :: 0 Trackbacks

          ORACLE Advanced SQL
          這篇文檔對(duì)深入研究Oracle有很大的用處。下面分別從以下幾個(gè)方面介紹。
          Transaction Management,joins,Subquerys,Optimizer,Indexs, Enhancement to other SQL operations
          如果您身邊就有Oracle系統(tǒng),那么您也可以通過(guò)運(yùn)行本篇文章的例子來(lái)做一些試驗(yàn)。
          l 事務(wù)處理(Transaction Management):
          事務(wù)的四大特性原子性、隔離性、獨(dú)立性,持續(xù)性。僅僅通過(guò)字面理解理解事務(wù)處理可能不是太直觀。那么,我們可以借用Jim Gray的描述來(lái)理解——“ 事務(wù)概念是分布式計(jì)算的一個(gè)重要抽象。在某一個(gè)層次實(shí)現(xiàn)了事務(wù),那么所有跟高層次上會(huì)有一個(gè)簡(jiǎn)化的失敗語(yǔ)義(要么全做,要么全不做),并且錯(cuò)誤處理也會(huì)簡(jiǎn)單得多。 ”
          在Oracle中通過(guò)設(shè)置ISOLATION LEVEL來(lái)避免幾乎所有數(shù)據(jù)庫(kù)都可能遇到的問(wèn)題——臟讀(一個(gè)事務(wù)讀取了另一個(gè)事務(wù)寫(xiě)的但是尚未提交的數(shù)據(jù))、不一致分析(一個(gè)事務(wù)前后讀取了不同的數(shù)據(jù))和幻象數(shù)據(jù)讀取(例如,我要執(zhí)行一個(gè)事務(wù)查詢,該事務(wù)查詢的任務(wù)是查詢13:00這個(gè)時(shí)間點(diǎn)的數(shù)據(jù),但是這個(gè)事務(wù)執(zhí)行時(shí)間可能需要一個(gè)小時(shí),但是在這期間由于還有正常的數(shù)據(jù)操作。所以,當(dāng)我在執(zhí)行完事務(wù)查詢時(shí)可能得到的數(shù)據(jù)就是14: 00的數(shù)據(jù)了,我們稱這種現(xiàn)象為幻象讀取)。
          我們知道標(biāo)準(zhǔn)的SQL99提出的事務(wù)隔離級(jí)別有四種,分別是READ UNCOMMITED、 READ COMMITED、REPEATABLE READ、SERIALIZABLE。ORACLE分別實(shí)現(xiàn)了READ COMMITED和 SERIALIZABLE。從這一點(diǎn)當(dāng)中我們可以看出,ORACLE默認(rèn)是避免臟讀的(當(dāng)然這也不一定如果在執(zhí)行完DML語(yǔ)句完以后如果沒(méi)有做 COMMIT操作可能也會(huì)出現(xiàn)臟讀的情況。但是,我還沒(méi)有遇到這種情況,讀者可以自己試驗(yàn)。)例句  SET TRANSACTION ISOLATION LEVEL [READ COMMITED| SERIALIZABLE]。 READ COMMITED可以避免臟讀,這也是ORACLE的默認(rèn)選項(xiàng)。但是,它不可避免我們提出的不一致分析和幻象讀取的問(wèn)題。那么我們可以將其隔離級(jí)別設(shè)置為SERIALIZABLE這樣我們就可以避免不一致分析和幻象讀取的問(wèn)題。那么,既然SERIALIZABLE級(jí)別這么好,那么我們是不是任何事務(wù)都要設(shè)置呢?答案是否定的,我們知道世界萬(wàn)物都是有其有利的一面就一定有其不利的一面。那么它的不利一面我們可以通過(guò)分析SERIALIZABLE的實(shí)現(xiàn)機(jī)制來(lái)看看。
           
          從圖中可以看出我們要實(shí)現(xiàn)SERIALIZABLE需要在系統(tǒng)中建立一個(gè)UNDO表空間來(lái)存放已經(jīng)過(guò)時(shí)的數(shù)據(jù)。我們可以根據(jù)我們最大執(zhí)行事務(wù)的執(zhí)行時(shí)間來(lái)估算一個(gè)UNDO段的大小。顯然,我們要是實(shí)現(xiàn)SERIALIZABLE就必須要另外的空間來(lái)建立UNDO表空間。也就是說(shuō)我們要實(shí)現(xiàn)SERIALIZABLE是通過(guò)空間來(lái)?yè)Q取避免不一致分析和幻象讀取的。
          l Joins操作
          在敘述下面的內(nèi)容之前先介紹一下我的機(jī)器環(huán)境:
          硬件環(huán)境:P4 2.8G,MEMORY 526M,80G硬盤(pán)。
          軟件環(huán)境:Windows 2000,ORACLE 9.2.0.1
          首先您要建立一個(gè)有足夠量數(shù)據(jù)的表,我建立一個(gè)有100萬(wàn)行以上的數(shù)據(jù)的表
          建表sql語(yǔ)句如下:
          DROP SEQUENCE SEQ_EMP
          /
          CREATE SEQUENCE SEQ_EMP START WITH 1000000
          /
          DROP TABLE EMPLOYEE
          /
          CREATE TABLE EMPLOYEE
          (EMPNO NUMBER(8),
           ENAME VARCHAR2(10),
           SAL   NUMBER(5),
           COMM  NUMBER(5),
           DEGREE NUMBER(1),
           HIREDATE DATE,
           DEPTNO NUMBER(2),
           ID_NO  VARCHAR2(18),
           BIRTHDAY DATE,
           CONSTRAINT PK_EMPLOYEE PRIMARY KEY(EMPNO)
          )
          /
          CREATE OR REPLACE TRIGGER TRI_INS_EMPLOYEE 
           BEFORE INSERT ON EMPLOYEE
           FOR EACH ROW
          DECLARE
           V_EN EMPLOYEE.EMPNO%TYPE;
          BEGIN
           V_EN:=:NEW.EMPNO;
           
            :NEW.COMM:=CEIL(:NEW.EMPNO/500000)*1000;
            :NEW.SAL:=(4-MOD(:NEW.EMPNO,4))*1000;
            :NEW.DEGREE:=CASE WHEN MOD(:NEW.EMPNO,4)=0 THEN NULL 
                             WHEN MOD(:NEW.EMPNO,10)<7 THEN 1
                             WHEN MOD(:NEW.EMPNO,10)<9 THEN 2
                             ELSE 3
                        END;
            :NEW.DEPTNO:=CASE WHEN :NEW.EMPNO<1000000 THEN 10
                              WHEN :NEW.EMPNO<1500000 THEN 20
                              WHEN :NEW.EMPNO<2000000 THEN 30
                              ELSE 40
                         END;
            :NEW.ID_NO:=REPLACE('21010119'||(7+MOD(V_EN,3))||MOD(V_EN,10)||TO_CHAR(1+MOD(V_EN,12),'00')||TO_CHAR(MOD(V_EN,20)+1,'00')||0||TO_CHAR(MOD(V_EN,100),'00')||MOD(V_EN,2),' ',NULL);

            :NEW.ID_NO:=REPLACE(:NEW.ID_NO,' ',NULL);

           :NEW.BIRTHDAY:=TO_DATE(SUBSTR(:NEW.ID_NO,7,8),'YYYYMMDD');
          END;
          /
          INSERT INTO EMPLOYEE (EMPNO)
           VALUES(SEQ_EMP.NEXTVAL)
          /
          /
          INSERT INTO EMPLOYEE (EMPNO) (SELECT SEQ_EMP.NEXTVAL FROM EMPLOYEE)
          /
              在做好以上準(zhǔn)備以后我們就可以進(jìn)行我們以后的實(shí)驗(yàn)了。
          在ORACLE中包括很多中連接方式EQUIJOINS、 SELF JOINS、 CARTESIAN PRODUCTS、 INNER JOINS、 OUTER JOINS、 ANTIJOINS, SEMIJOINS。
          由于我的個(gè)人水平有限我不能全部介紹,我只能將自己理解的內(nèi)容介紹個(gè)大家。
          下面就僅僅介紹有關(guān)反連接的問(wèn)題。
          反連接(ANTIJOINS)通過(guò) SET AUTOTRACE TRACE打開(kāi)跟蹤執(zhí)行策略我們分別比較以下的語(yǔ)句。
          ============================================================
          SELECT ENAME FROM EMP WHERE DEPTNO NOT IN(
             SELECT DEPTNO FROM DEPT WHERE LOC='NEW YORK');

          SELECT ENAME FROM EMP WHERE NOT EXISTS(
             SELECT 1 FROM DEPT WHERE DEPT.DEPTNO=EMP.DEPTNO AND LOC='NEW YORK');
          我們可以發(fā)現(xiàn),運(yùn)用NOT IN 要比 NOT EXISTS要快一些但是有個(gè)比較大的問(wèn)題,就是在上面的實(shí)驗(yàn)中我們假設(shè)EMP表和DEPT表中沒(méi)有字段內(nèi)容為NULL的數(shù)據(jù)。如果我們要查詢的表中有為NULL的數(shù)據(jù)的時(shí)候,那么我們?cè)谶\(yùn)用NOT IN操作會(huì)發(fā)現(xiàn)查出的數(shù)據(jù)是不準(zhǔn)確的情況。結(jié)論:在您為選擇 NOT IN 和 NOT EXISTS時(shí)而苦惱時(shí)請(qǐng)優(yōu)先選擇運(yùn)用NOT EXISTS語(yǔ)句。
          接下來(lái)是有關(guān)JOIN的一些實(shí)驗(yàn)。
          下面我們做如下試驗(yàn)
          比較JOIN與IN & 子查詢以及EXISTS & 子查詢 它們?nèi)咧g的執(zhí)行效率

          --12.04秒
          SELECT * FROM EMPLOYEE A JOIN DEPT B ON A.DEPTNO=B.DEPTNO WHERE B.LOC='NEW YORK';
          --4.02秒
          SELECT * FROM EMPLOYEE WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT WHERE LOC='NEW YORK');
          --4.02秒
          SELECT * FROM EMPLOYEE WHERE EXISTS (SELECT 1 FROM DEPT WHERE EMPLOYEE.DEPTNO=
          DEPT.DEPTNO AND LOC='NEW YORK');

          通過(guò)上面的實(shí)驗(yàn)我們可以得出運(yùn)用 IN或者 EXISTS操作的效率要高于運(yùn)用JOIN操作的語(yǔ)句。我們知道ORACLE在執(zhí)行用戶的查詢請(qǐng)求是 ORACLE會(huì)選擇一些查詢策略來(lái)完成操作,但是有時(shí)候ORACLE的選擇是很愚蠢的。比如,我要做一個(gè)簡(jiǎn)單的連接查詢的操作 SELECT * FROM EMPLOYEE A JOIN EMP B ON A.EMPNO=B.EMPNO;通過(guò)跟蹤執(zhí)行策略我們會(huì)發(fā)現(xiàn) ORACLE可能選擇一個(gè)很慢的策略。那么,遇到這種情況該如何處理呢?我們可以通過(guò)兩種方式來(lái)處理。第一,我們可以為該查詢添加注釋讓它執(zhí)行我們要求的策略。比如我們可以這么寫(xiě)SELECT /*+ use_rule…(EMPLOYEE EMP)  */* FROM EMPLOYEE A JOIN EMP B ON A.EMPNO=B.EMPNO;(use_rule您可以選擇您需要的策略。例如HASH JOIN等其他的執(zhí)行策略)。例如,我們?yōu)樯厦孢@個(gè)查詢語(yǔ)句選擇下面兩種策略:
          1. SELECT /*+USE_HASH(EMPLOYEE,DEPT)*/* FROM EMPLOYEE,DEPT WHERE EMPLOYEE.DEPTNO=DEPT.DEPTNO;
          2. SELECT /*+USE_MERGE(EMP,DEPT)*/* FROM EMP JOIN DEPT ON EMP.DEPTNO=DEPT.DEPTNO;
          第二,就是,為該表添加統(tǒng)計(jì)資料分析信息ANALYZE TABLE EMPLOYEE COMPUTE STATISTICS;這樣我們?cè)趯?duì)該表查詢時(shí)ORACLE就可以選擇一個(gè)較優(yōu)的執(zhí)行策略了。
          通過(guò)以上的實(shí)驗(yàn)我們可以得出以下結(jié)論:ORACLE系統(tǒng)在執(zhí)行DML語(yǔ)句的時(shí)候可能不會(huì)選擇其最優(yōu)的執(zhí)行策略,這需要我們通過(guò)為DML語(yǔ)句添加注釋強(qiáng)迫 ORACLE選擇一個(gè)我們?nèi)藶橹付ǖ膱?zhí)行策略或者通過(guò)為表、試圖等添加統(tǒng)計(jì)資料分析信息來(lái)使ORACLE在執(zhí)行DML時(shí)選擇較優(yōu)的執(zhí)行策略。
          l 子查詢(Subquery)
          這部分內(nèi)容我們將會(huì)做一系列的實(shí)驗(yàn),最后我們從實(shí)驗(yàn)的結(jié)果當(dāng)中得出使用子查詢的情況。我并不是要介紹如何使用子查詢,而是通過(guò)使用子查詢來(lái)分析ORACLE的執(zhí)行策略等信息。
          實(shí)驗(yàn)1:比較JOIN、IN以及EXISTS三種執(zhí)行方式的執(zhí)行時(shí)間
          --12.04秒
          SELECT * FROM EMPLOYEE A JOIN DEPT B ON A.DEPTNO=B.DEPTNO WHERE B.LOC='NEW YORK';
          --4.02秒
          SELECT * FROM EMPLOYEE WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT WHERE LOC='NEW YORK');
          --4.02秒
          SELECT * FROM EMPLOYEE WHERE EXISTS (SELECT 1 FROM DEPT WHERE EMPLOYEE.DEPTNO=
          DEPT.DEPTNO AND LOC='NEW YORK');

          通過(guò)實(shí)驗(yàn)1我們得出結(jié)論:使用EXISTS、IN語(yǔ)句要比JOIN語(yǔ)句執(zhí)行效率高。


          實(shí)驗(yàn)2:表較幾個(gè)語(yǔ)句的查詢效率

          --27.07秒
          SELECT * FROM EMPLOYEE 
          WHERE SAL>(SELECT AVG(SAL) FROM EMPLOYEE);

          --27.03秒
          SELECT * FROM EMPLOYEE A 
          WHERE SAL>
          (SELECT AVG(SAL) FROM EMPLOYEE B WHERE A.DEPTNO=B.DEPTNO);

          --39秒
          SELECT * FROM EMPLOYEE A,
          (SELECT DEPTNO,AVG(SAL) AS SAL1 
          FROM EMPLOYEE GROUP BY DEPTNO) B
          WHERE A.DEPTNO=B.DEPTNO AND 
          A.SAL>B.SAL1 WHERE SAL>(SELECT AVG(SAL)
           FROM EMPLOYEE B WHERE A.DEPTNO=B.DEPTNO);

          --22.05秒
          SELECT YY.EMPNO,YY.ENAME,YY.DEPTNO,YY.SAL,
          YY.SAL-XX.SAL1 AS NEWSAL FROM EMPLOYEE YY,
          (SELECT DEPTNO,AVG(SAL) AS SAL1 FROM EMPLOYEE 
          GROUP BY DEPTNO) XX 
          WHERE YY.DEPTNO=XX.DEPTNO AND YY.SAL>XX.SAL1;

          --26.06秒
          SELECT YY.EMPNO,YY.ENAME,YY.DEPTNO,YY.SAL,
          YY.SAL-XX.SAL1 AS NEWSAL,XX.SAL1,XX.SAL2 
          FROM EMPLOYEE YY,
          (SELECT DEPTNO,AVG(SAL) AS SAL1,MAX(SAL) AS SAL2 FROM EMPLOYEE GROUP BY DEPTNO) XX 
          WHERE YY.DEPTNO=XX.DEPTNO AND YY.SAL>XX.SAL1;

          通過(guò)實(shí)驗(yàn)2我們可以得出結(jié)論:
          1. 在執(zhí)行子查詢盡可能將語(yǔ)句向連接的方式上靠。
          2. 在子查詢中可以將相關(guān)子查詢轉(zhuǎn)為連接的形式。
          3. 在做子查詢時(shí)select * 要比select 列名 的語(yǔ)句慢得多。
          另外,在ORACLE中我們也可以運(yùn)用WITH語(yǔ)句代替子查詢以提高語(yǔ)句的可讀性。下面是運(yùn)用with語(yǔ)句的例子。
          實(shí)驗(yàn)3:運(yùn)用WITH語(yǔ)句
          WITH 
            SUB_TABLE AS (
              SELECT DEPTNO,AVG(SAL) AS SAL1,MAX(SAL) AS SAL2 FROM EMPLOYEE GROUP BY DEPTNO)
          SELECT A.EMPNO,A.ENAME,E.DEPTNO,A.SAL,A.SAL-SUB_TABLE.SAL11,
                  SUB_TABLE.SAL1,SUB_TABLE.SAL2
           FROM EMPLOYEE A,SUB_TABLE WHERE 
              A.DEPTNO=SUB_TABLE.DEPTNO AND A.SAL>SUB_TABLE.SAL1;

          WITH 
          SUB_TABLE AS (
            SELECT DEPTNO,AVG(SAL) AS SAL1,MAX(SAL) AS SAL2 
          FROM EMPLOYEE GROUP BY DEPTNO)
          SELECT *     
          FROM EMPLOYEE,SUB_TABLE WHERE 
          EMPLOYEE.DEPTNO=SUB_TABLE.DEPTNO AND EMPLOYEE.SAL>SUB_TABLE.SAL1;
          l 索引(Index)
          我們知道索引的使用可以提高查詢速度,這是我以前對(duì)索引的理解。但是使用索引也有副作用。我們知道在ORACEL中查詢一條記錄時(shí)如果沒(méi)有索引的情況下,它的執(zhí)行方式如下所示
           
          如果我要做查詢 select C2 from 表 where C2=6 那么在ORACLE中系統(tǒng)會(huì)從最后一條掃描一直將表整個(gè)掃描一遍以后,這個(gè)查詢動(dòng)作才算完成。從中我們可以看出沒(méi)有索引的表的記錄是無(wú)序存放的。相反如果我們對(duì)這個(gè)表在列C2建立一個(gè)索引以后它的查詢執(zhí)行如下所示:
           
          我們可以看到在查詢是系統(tǒng)如果選用該索引的話那么ORACLE將會(huì)查找一些有序的數(shù)據(jù),那么我們的查詢速度將會(huì)大大地提高。
          上面我們描述的是查詢一列數(shù)據(jù)時(shí)的情況,那么如果查詢所有數(shù)據(jù)呢,請(qǐng)看下圖所示
           
          如果我們帶上索引查詢,ORACLE首先會(huì)找到索引然后找到在基表中記錄的位置。顯然這樣比直接在表中查詢要慢。但是這個(gè)結(jié)論對(duì)不對(duì)呢?下面將會(huì)做一些實(shí)驗(yàn)說(shuō)明這個(gè)問(wèn)題。
          在進(jìn)行以下的實(shí)驗(yàn)值前首先,您需要作如下準(zhǔn)備:
          在EMPLOYEE表的SAL列上建立一個(gè)索引
          CREATE INDEX IND_EMPLOYEE_SAL ON EMPLOYEE(SAL);
          在EMPLOYEE表的SAL,DEGREE兩個(gè)列上建立一個(gè)聯(lián)合索引
          CREATE INDEX IND_EMPLOYEE_SALandDEGREE 
          ON EMPLOYEE(SAL,DEGREE);
          實(shí)驗(yàn)1:使用索引與不使用索引

          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
             EMPLOYEE WHERE SAL=1000;
          --13.03秒
          SELECT /*+NO_INDEX(EMPLOYEE)*/
          * FROM 
             EMPLOYEE WHERE SAL=1000;
          利用索引查詢的時(shí)間的數(shù)據(jù)由于我的粗心大意弄丟了,但是我記得運(yùn)用索引的查詢要比沒(méi)有運(yùn)用索引的查詢要慢一些。

          實(shí)驗(yàn)2:?jiǎn)瘟兴饕c多列索引之間的區(qū)別
          //////////////////////////////////////////////////
          ////單列索引的情況
          --13.04秒 USE INDEX IND_EMPLOYEE_SAL
          SELECT * FROM EMPLOYEE WHERE SAL=1000;

          --13.04秒
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000;

          --19秒
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SALandDEGREE)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000;

          //////////////////////////////////////////////////
          ////單列索引的情況
          --22秒   USE TABLE ACCESS 
          SELECT * FROM EMPLOYEE WHERE DEGREE=1;




          --22秒     USE TABLE ACCESS 
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE DEGREE=1;

          --29秒
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SALandDEGREE)*/
          * FROM 
              EMPLOYEE WHERE DEGREE=1;

          //////////////////////////////////////////////////
          ////多列索引的情況
          --8秒 USE INDEX IND_EMPLOYEE_SALandDEGREE
          SELECT * FROM EMPLOYEE WHERE SAL=1000 AND DEGREE=1;

          --9秒 USE INDEX IND_EMPLOYEE_SAL
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000 AND DEGREE=1;

          --8秒 USE INDEX IND_EMPLOYEE_SALandDEGREE
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SALandDEGREE)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000 AND DEGREE=1;

          //////////////////////////////////////////////////
          ////多列索引的情況
          --8秒 USE INDEX IND_EMPLOYEE_SALandDEGREE
          SELECT * FROM EMPLOYEE WHERE DEGREE=1 AND SAL=1000;

          --9秒 USE INDEX IND_EMPLOYEE_SAL
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE DEGREE=1 AND SAL=1000;



          --8秒 USE INDEX IND_EMPLOYEE_SALandDEGREE
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SALandDEGREE)*/
          * FROM 
              EMPLOYEE WHERE DEGREE=1 AND SAL=1000;

          //////////////////////////////////////////////////
          ////多列索引的情況
          --3.4秒 USE INDEX IND_EMPLOYEE_SAL
          SELECT * FROM EMPLOYEE WHERE SAL=1000 AND COMM=2000;

          --3.4秒 USE INDEX IND_EMPLOYEE_SAL
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000 AND COMM=2000;

          --8秒 USE INDEX IND_EMPLOYEE_SALandDEGREE
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SALandDEGREE)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000 AND COMM=2000;

          實(shí)驗(yàn)3:CLUSTER
          CREATE CLUSTER DD_DDMX (ID NUMBER(5)) 
          TABLESPACE users ;

          CREATE TABLE DD
          (ID NUMBER(5) PRIMARY KEY,
           DDTIME DATE)
          CLUSTER DD_DDMX(ID);

          CREATE TABLE DDMX
          (ID NUMBER(5),
           PROID VARCHAR2(20),
           PRO VARCHAR2(30))
          CLUSTER DD_DDMX(ID);

          CREATE INDEX DD_DDMX_index 
            ON CLUSTER DD_DDMX
            TABLESPACE indx;
          實(shí)驗(yàn)4:BITMAP INDEX
          CREATE BITMAP INDEX INDBIT_EMPLOYEE_SAL ON EMPLOYEE(SAL)
          TABLESPACE INDX;

          --13.04秒
          SELECT /*+INDEX (EMPLOYEE IND_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000;

          --12.07秒  USE INDBIT_EMPLOYEE_SAL
          SELECT /*+INDEX (EMPLOYEE INDBIT_EMPLOYEE_SAL)*/
          * FROM 
              EMPLOYEE WHERE SAL=1000;
          實(shí)驗(yàn)5:分區(qū)索引
          分區(qū)索引的原理如下所示:

           
          我們知道在磁道在磁盤(pán)上尋址的I/O操作的開(kāi)銷是相當(dāng)大的,如果我們建立了分區(qū)索引將其索引放在不同的磁盤(pán)上那么可以大大節(jié)省I/0開(kāi)銷提高我們的查詢速度。


          ALTER TABLE EMP
            ADD CONSTRAINT PK_EMP
          PRIMARY KEY(EMPNO) USEING INDEX TABLESPACE INDX;
          ALTER TABLE EMPLOYEE
            DROP CONSTRAINT PK_EMPLOYEE;

          ALTER TABLE EMPLOYEE
            ADD CONSTRAINT PK_EMPLOYEE
          PRIMARY KEY(EMPNO) USING INDEX TABLESPACE INDX;

          CREATE INDEX IND_EMPLOYEE_BIRTHDAY 
            ON EMPLOYEE(BIRTHDAY)
          GLOBAL PARTITION BY RANGE(BIRTHDAY)
          (PARTITION P1 VALUES LESS THAN (DATE '1980-01-01') TABLESPACE USERS,
           PARTITION P2 VALUES LESS THAN (DATE '1990-01-01') TABLESPACE INDX,
           PARTITION P3 VALUES LESS THAN (DATE '2000-01-01') TABLESPACE USERS,
           PARTITION P4 VALUES LESS THAN(MAXVALUE) TABLESPACE INDX);

          CREATE TABLE wage
              ( empno              NUMBER, 
                year_month     INT NOT NULL,
                opdate              DATE)
            PARTITION BY RANGE (year_month)
              ( PARTITION wage_q1 VALUES LESS THAN (199701) 
                  TABLESPACE users,
                PARTITION wage_q2 VALUES LESS THAN (199702) 
                  TABLESPACE users,
                PARTITION wage_q3 VALUES LESS THAN (199703) 
                  TABLESPACE users,
                PARTITION sales_q4 VALUES LESS THAN (199704) 
                  TABLESPACE users); 
          -- Local Partitioned Index
          CREATE INDEX IND_WAGE(year_month)
          LOCAL
           (PARTITION P1,
            PARTITION P2,
            PARTITION P3,
            PARTITION P4);

          以上是我們關(guān)于索引的一些實(shí)驗(yàn)信息,通過(guò)該實(shí)驗(yàn)我們可以得出以下結(jié)論:
          1. 索引是為了提高查詢速度,排序數(shù)據(jù)。
          2. 索引不見(jiàn)得總能提高速度。
          3. 查詢結(jié)果占總記錄數(shù)越少越可以用索引。
          4. 分區(qū)索引 將數(shù)據(jù)分散到多個(gè)物理位置上來(lái)提高其IO的功能。
          5. 全表掃描有時(shí)性能也不錯(cuò)。
          6. 在統(tǒng)計(jì)資料不完整時(shí)未必選擇正確的索引。
          7. 有時(shí)候添加索引可能會(huì)降低速度。
          8. 索引適合于結(jié)果行占所有行的比很小的時(shí)候運(yùn)用 經(jīng)驗(yàn)值為比為5%左右為好,但是還可能還有許多其他情況影響查詢速度。
          9. 值是唯一的時(shí)候運(yùn)用索引可以提高查詢速度。
          10. 當(dāng)統(tǒng)計(jì)資料不完整的時(shí)候,查詢速度會(huì)很慢。可以通過(guò)對(duì)索引進(jìn)行統(tǒng)計(jì)分析來(lái)調(diào)整。
          11. where條件中列的順序不影響其查詢速度。
          12. 多列索引中的后面的列最好不要運(yùn)用索引。
          13. 多列索引中其列的順序是不同的。不同的順序可能造成查詢速度不一樣。
          14. 多列索引中第一列是有順序的,但是其后面的列是沒(méi)有順序。
          15. where條件查詢的列中只要有索引,那么oracle會(huì)選擇運(yùn)用索引。
          16. 當(dāng)查詢的結(jié)果大于所有行的記錄數(shù)的10%時(shí),那么最好不要運(yùn)用索引。
          17. 小表用索引反而會(huì)降低速度。
          18. 索引需要DBA經(jīng)常刪除重新建立,因?yàn)樗饕Y(jié)構(gòu)變化過(guò)多可能造成以后的查詢變慢。
          19. DML語(yǔ)句會(huì)影響索引結(jié)構(gòu)大的變化,所以經(jīng)常作DML語(yǔ)句的表可以考慮不用索引。
          20. CLUSTER索引僅僅用于只讀的的表多用于cluster表。
          21. 維圖索引多用于行多但是值的類型很少的情況。
          22. 表和索引要放在兩個(gè)不同的表空間上。
          l Enhancement to other SQL operations
          下面的內(nèi)容是介紹一些有關(guān)ORACLE技術(shù)的一些比較雜的內(nèi)容。
          一、 層次查詢
          在同一個(gè)表中的不同記錄有著直接或者間接的關(guān)系。
          例如,我們查詢EMP表的信息如下:
            從圖中我們可以看到ENAME是’SMITH’的領(lǐng)導(dǎo)是編號(hào)為7902的人。而7902的領(lǐng)導(dǎo)是編號(hào)為7566的人。7566的領(lǐng)導(dǎo)是編號(hào)為7839的人。那么這樣的關(guān)系我們就可以通過(guò)運(yùn)用層次查詢就可以查詢出來(lái)。
          層次查詢實(shí)驗(yàn)
          SELECT * FROM EMP
          CONNECT BY PRIOR MGR=EMPNO
            START WITH ENAME='SMITH';

          SELECT * FROM EMP
          CONNECT BY PRIOR EMPNO=MGR
            START WITH ENAME='SMITH';

          SELECT LEVEL,ENAME FROM EMP
          CONNECT BY PRIOR EMPNO=MGR
            START WITH ENAME='SMITH';

          SELECT MID,PARENTID,AMOUNT,SYS_CONNECT_BY_PATH(MID,'/') PATH
          FROM MATERIAL
             WHERE STOCK=1
                  CONNECT BY PRIOR MID=PARENTID
                                    START WITH MID='1001';
          二、 分組查詢
          就是我們平時(shí)運(yùn)用的group語(yǔ)句.
          分組查詢實(shí)驗(yàn)
          SELECT DEPTNO,JOB,COUNT(*)
            FROM EMP
             GROUP BY ROLLUP(DEPTNO,JOB);

          SELECT DEPTNO,JOB,COUNT(*)
            FROM EMP
             GROUP BY cube(DEPTNO,JOB);

          SELECT DEPTNO,JOB,TO_CHAR(HIREDATE,'YYYY'),COUNT(*)
            FROM EMP
             GROUP BY GROUPING SETS((DEPTNO,JOB),
          (DEPTNO,TO_CHAR(HIREDATE,'YYYY')),());
          三、 并行執(zhí)行的SQL語(yǔ)句
          并行執(zhí)行的SQL語(yǔ)句實(shí)驗(yàn)

          --19.09秒
          ALTER TABLE EMPLOYEE PARALLEL (DEGREE 1);
          SELECT * FROM EMPLOYEE WHERE SAL=1000;

          --18秒
          ALTER TABLE EMPLOYEE PARALLEL (DEGREE 2);
          SELECT * FROM EMPLOYEE WHERE SAL=1000;

          --19秒
          ALTER TABLE EMPLOYEE PARALLEL (DEGREE 4);
          SELECT * FROM EMPLOYEE WHERE SAL=1000;

          --20.3秒
          ALTER TABLE EMPLOYEE PARALLEL (DEGREE 6);
          SELECT * FROM EMPLOYEE WHERE SAL=1000;

          --19秒
          ALTER TABLE EMPLOYEE PARALLEL (DEGREE 10);
          SELECT * FROM EMPLOYEE WHERE SAL=1000;
          四、 實(shí)體化視圖
          在進(jìn)行以下的實(shí)驗(yàn)之前,我們需要作如下準(zhǔn)備:
          建立一個(gè)普通視圖:
          CREATE VIEW V_EMPLOYEE
          AS SELECT SUM(SAL) AS C FROM EMPLOYEE;
          建立實(shí)體化視圖:
          CREATE MATERIALIZED VIEW 名字 AS 查詢;

          CREATE MATERIALIZED VIEW V_M_EMPLOYEE 
                  AS SELECT SUM(COMM) AS C FROM EMPLOYEE;
          實(shí)體化視圖與普通視圖的比較

          --20.04秒
          SELECT * FROM V_EMP;

          --0.01秒
          SELECT * FROM V_M_EMPLOYEE;

          通過(guò)以上的實(shí)驗(yàn),我們可以得出結(jié)論:實(shí)體化視圖不包含新的數(shù)據(jù),查詢速度很快。
          如果需要實(shí)體化視圖包含新的數(shù)據(jù)我們可以通過(guò)
          手工刷新:
             EXEC DBMS_MVIEW.REFRESH('V_M_EMPLOYEE','CF');  CF -- 完全快速刷新
                  自動(dòng)刷新:
          //每個(gè)表可以創(chuàng)建一個(gè)實(shí)體化視圖日志。
          CREATE MATERIALIZED VIEW LOG ON 表名
          WITH(列名列表),
              ROWID INCLUDING NEW VALUES;
                 //創(chuàng)建自動(dòng)刷新的實(shí)體化視圖
                  CREATE MATERIALIZED VIEW 名字
                  BUILD IMMEDIATE
          〔REFRESH FAST||REFRESH COMPLETE〕 ON COMMIT 
          --REFRESH FAST 僅僅支持Insert語(yǔ)句的刷新
                       AS ...
          運(yùn)用REFRESH COMPLETE 適合于update,delete操作較少的表。并且試驗(yàn)發(fā)現(xiàn)運(yùn)用
          REFRESH COMPLETE時(shí)COMMIT操作會(huì)很慢!
          五、 查詢重寫(xiě)技術(shù)(QUERY REWRITE)
          表面上看是在查詢表,但是oracle實(shí)際上是去查詢實(shí)體化視圖的技術(shù)。
          在下面的試驗(yàn)中我們也可以看出實(shí)際上無(wú)論是查詢表還是視圖oracle都會(huì)轉(zhuǎn)向查詢實(shí)體化視圖 MV_EMP。
          對(duì)于反復(fù)執(zhí)行的匯總查詢存放起來(lái)、節(jié)省查詢時(shí)間,多用于匯總的計(jì)算。
                
          //創(chuàng)建查詢重寫(xiě)技術(shù)
                  CREATE MATERIALIZED VIEW 名字
                         BUILD IMMEDIATE
              〔REFRESH FAST||REFRESH COMPLETE〕 ON COMMIT 
          --REFRESH FAST 僅僅支持Insert語(yǔ)句的刷新
                  ENABLE QUERY REWRITE
                       AS ...
          運(yùn)用查詢重寫(xiě)技術(shù)的例子
          CREATE MATERIALIZED VIEW MV_EMP
              BUILD IMMEDIATE
                  REFRESH FAST ON COMMIT
                     ENABLE QUERY REWRITE
                          AS SELECT SUM(COMM) AS C FROM EMPLOYEE;

          ALTER SESSION SET QUERY_REWRITE_ENABLED=TRUE;

          --0.00秒
          -- EXPLAN: TABLE ACCESS (FULL) OF 'MV_EMP'
          SELECT SUM(COMM) FROM EMPLOYEE;

          --0.00秒
          --EXPLAN: TABLE ACCESS (FULL) OF 'MV_EMP'
          SELECT * FROM V_MEP;

          經(jīng)過(guò)手工刷新以后查詢速度會(huì)變慢!
          六、 分布式技術(shù)
          分布式技術(shù)包括分布式數(shù)據(jù)庫(kù)、DB LINK、分布式查詢以及分布式事務(wù)管理。這里我們僅僅介紹有關(guān)DB LINK的內(nèi)容。
          DB LINK的實(shí)驗(yàn)
          GRANT CREATE DATABALSE LINK TO SCOTT;

          CONN SCOTT/TIGER

          CREATE DATABASLE LINK OEM_LINK
            CONNECT TO SCOTT IDENTIFIED BY TIGER
              USING 'AAA';

          SELECT ENAME FROM EMP@OEM_LINK

          通過(guò)以上實(shí)驗(yàn)我們得出結(jié)論:如果在執(zhí)行分布式事務(wù)的時(shí)候那么網(wǎng)絡(luò)一定要保持正常,如果網(wǎng)絡(luò)斷開(kāi)就會(huì)發(fā)生死鎖的情況。這時(shí)要進(jìn)行在服務(wù)端和客戶端分別將鎖刪除即可解決。


          七、 DML語(yǔ)句
          INSERT INTO ALL
             INTO 表1 VALUES(列名列表)
             ...
             INTO 表2 VALUES(列名列表)
          子查詢;

          DML語(yǔ)句的實(shí)驗(yàn)
          CREATE TABLE TEST_DWRS
          (DEPTNO NUMBER(2),
           RENSHU NUMBER(10)
          );

          CREATE TABLE TEST_DWGZ
          (DEPTNO NUMBER(2),
           AVGSAL NUMBER(7,2)
          );

          INSERT ALL
            INTO TEST_DWRS VALUES (DEPTNO,RENSHU)
            INTO TEST_DWGZ VALUES (DEPTNO,AVGSAL)
          SELECT DEPTNO DEPT,COUNT(*) REN_SHU,AVG(SAL) AVG_SAL
            FROM EMP GROUP BY DEPTNO;

          INSERT ALL
            WHEN 條件1 THEN
               INTO 表名1 VALUES(列名列表)
            ...
            WHEN 條件2 THEN
               INTO 表名2 VALUES(列名列表)
          子查詢;
          INSERT ALL
            WHEN REN_SHU>1000 THEN
             INTO TEST_DWRS VALUES(DEPTNO,RENSHU)
            WHEN AVG_SAL>2000 THEN
          INTO TEST_DWGZ VALUES(DEPTNO,AVGSAL)
          SELECT DEPTNO,COUNT(*) REN_SHU,AVG(SAL) AVG_SAL
            FROM EMP GROUP BY DEPTNO;
          八、 外部表(EXTERNAL TABLES)
          外部表是我們應(yīng)用中可能會(huì)常常碰到的問(wèn)題之一。例如,我需要將文本文件的數(shù)據(jù)導(dǎo)入到ORACLE數(shù)據(jù)庫(kù)中我們就可以利用此項(xiàng)技術(shù)。
          假設(shè)外部文件有如下信息:
           
          我們可以將此信息轉(zhuǎn)為以逗號(hào)分割的一些信息存為test.txt文本文件。
          然后我們?cè)贠RACLE中建立一個(gè)訂單表BILL(BILL_ID 訂單編號(hào),BILL_D訂單日期)
          以及訂單明細(xì)表BILL_MX(BILL_ID 訂單編號(hào),P 產(chǎn)品編號(hào),PAMOUNT 產(chǎn)品數(shù)量)。
          我們下面的實(shí)驗(yàn)就是通過(guò)將外部的文件信息導(dǎo)入到BILL表和BILL_MX表中去。
          首先簡(jiǎn)單介紹一下創(chuàng)建外部表的方法:
          第一步,創(chuàng)建一個(gè)DIRECTORY對(duì)象
          CREATE DIRECTORY 名字 AS '路徑'
                  CREATE DIRECTORY test_dir AS 'D:\1';
          第二步,創(chuàng)建外部表

          外部表的實(shí)驗(yàn)
          //創(chuàng)建外部表
          CREATE TABLE BILL_EXTERNAL
          (BILL_ID VARCHAR2(8),
           BILL_D DATE,
           P1 VARCHAR2(10),
           P1_AMOUNT VARCHAR2(10),
           P2 VARCHAR2(10),
           P2_AMOUNT VARCHAR2(10),
           P3 VARCHAR2(10),
           P3_AMOUNT VARCHAR2(10))
          ORGANIZATION EXTERNAL
          (TYPE ORACLE_LOADER
           DEFAULT DIRECTORY test_dir
           ACCESS PARAMETERS
            (RECORDS DELIMITED BY NEWLINE
             FIELDS TERMINATED BY ','
             (BILL_ID CHAR,
              BILL_D CHAR DATE_FORMAT 
                  DATE MASK "YYYYMMDD",
              P1 CHAR,
              P1_AMOUNT CHAR,
              P2 CHAR,
              P2_AMOUNT CHAR,
              P3 CHAR,
              P3_AMOUNT CHAR
                
            
            LOCATION ('TEST.TXT')
          );

          //導(dǎo)入數(shù)據(jù)
          INSERT ALL
            WHEN BILL_ID<>0 THEN
             INTO BILL VALUES(BILL_ID,BILL_D)
            WHEN P1<>0 THEN
                  INTO BILL_MX VALUES(BILL_ID,P1,P1_AMOUNT)
            WHEN P2<>0 THEN
                  INTO BILL_MX VALUES(BILL_ID,P2,P2_AMOUNT)
            WHEN P3<>0 THEN
                  INTO BILL_MX VALUES(BILL_ID,P3,P3_AMOUNT)
          SELECT * FROM BILL_EXTERNAL;
          九、 日期類型
          日期類型的實(shí)驗(yàn)

          SELECT SYSDATE + TO_YMINTERVAL('01-02') FROM DUAL;
          SELECT SYSTIMESTAMP + TO_DSINTERVAL('01 01:02:01') FROM DUAL;

          SELECT SYSTIMESTAMP + TO_YMINTERVAL('10-3') +
                 TO_DSINTERVAL('05 07:00:00') FROM DUAL;
          十、 自定義數(shù)據(jù)類型
          自定義數(shù)據(jù)類型我們將會(huì)簡(jiǎn)單地介紹有關(guān)記錄和集合的內(nèi)容。
          記錄類型的實(shí)驗(yàn)

          ORACLE 對(duì)象可以理解為JAVA中的類的概念。
             


          CREATE TYPE T_REC AS OBJECT(
                 A NUMBER,
                 B NUMBER
             ;

          CREATE TABLE RTEST(A NUMBER,
                             B NUMBER,
                             C T_REC);

          INSERT INTO RTEST VALUES(1,2,T_REC(10,20));

          CREATE TYPE EMP_INFORMATION AS OBJECT(
              ADDR VARCHAR2(50),
              EMAIL VARCHAR2(50),
              PHONE VARCHAR2(11));

          CREATE TABLE EMP1(
            EMPID VARCHAR2(10),
            EMPNAME VARCHAR2(20),
            INFO EMP_INFORMATION);

          INSERT INTO EMP1(EMPID,EMPNAME,INFO) 
            SELECT '0001','SHI',
                  EMP_INFORMATION('DALIAN','SHIBU@163.COM','240560560') 
             FROM DUAL;
          集合類型的實(shí)驗(yàn)
          CREATE TYPE 類型名 AS TABLE OF 元素類型名
          CREATE TABLE...
            (....
              C1 (NESTED TABLE的類型名),
             ...
            
          CREATE TYPE NTA AS TABLE OF NUMBER;
          CREATE TABLE NTEST (A NUMBER,
            B NUMBER,
            C NTA)
          NESTED TABLE C STORE AS NTT;
          INSERT INTO NTEST VALUES(1,2,NTA(100,200,300));
          實(shí)驗(yàn):創(chuàng)建一個(gè)工資表 職工編號(hào),工資,補(bǔ)貼其中補(bǔ)貼是嵌套表,每個(gè)職工的都可以有多種補(bǔ)貼數(shù)量不定
          CREATE TYPE BT_VALUE AS OBJECT(BT_NAME VARCHAR2(20),
                                          BT_JE NUMBER(4));

          CREATE TYPE BT_LIST AS TABLE OF BT_VALUE;

          CREATE TABLE GZB(EMPNO NUMBER(4),SAL NUMBER(4), BT BT_LIST)
             NESTED TABLE BT STORE AS BT_TAB;

          INSERT INTO GZB VALUES(1001,1000,BT_LIST(BT_VALUE('JTFEE',500),
                                                   BT_VALUE('TELFEE',200)
                                                  
                                 
          UPDATE TABLE(SELECT BT FROM GZB WHERE EMPNO=1001) SET BT_JE=150 WHERE BT_NAME='JTFEE';
           
          SELECT EMPNO,SAL,(SELECT SUM(BT_JE) FROM TABLE(BT)) BT_JE
           FROM GZB;

          至此,有關(guān)的ORACLE Advanced  SQL的內(nèi)容就介紹完了。通過(guò),這篇文檔的總結(jié)我也體會(huì)到數(shù)據(jù)庫(kù)的學(xué)習(xí)與應(yīng)用我們還有很長(zhǎng)的路要走。

          2005-9-3
          分布式計(jì)算---JAVA技術(shù)

          運(yùn)用JAVA技術(shù)實(shí)現(xiàn)分布式計(jì)算



          目前,運(yùn)用JAVA技術(shù)實(shí)現(xiàn)分布式計(jì)算的技術(shù)主要有RMI、CORBA以及Scoket通信三方面技術(shù),下面就這三方面技術(shù)分別做一下比較。



               RMI



          RMI技術(shù)遠(yuǎn)程調(diào)用,是基于RPC技術(shù)發(fā)展而來(lái)的。其開(kāi)發(fā)過(guò)程基本由下面幾個(gè)過(guò)程



          1.         定義遠(yuǎn)程接口



          2.         實(shí)現(xiàn)遠(yuǎn)程接口



          3.         編寫(xiě)使用遠(yuǎn)程對(duì)象



          4.         生成stub(客戶代理)以及skeletom(服務(wù)器實(shí)體)



          5.         啟動(dòng)注冊(cè)表并且注冊(cè)對(duì)象



          6.         運(yùn)行服務(wù)器和客戶



          下面就分別對(duì)其簡(jiǎn)單介紹



          1.        定義遠(yuǎn)程接口



           



          package shi.rmi;



          public interface RemoteShiInterface extends java.rmi.Remote



          {



                    String message(String message)throws java.rmi.RemoteException;



          }



          接口必須繼承于java.rmi.Remote;并且定義方法



          必須拋出java.rmi.RemoteExcetption的異常。



          2.        實(shí)現(xiàn)遠(yuǎn)程接口



           



          package shi.rmi;



          import java.rmi.Naming;



          import java.rmi.server.UnicastRemoteObject;



          import java.rmi.RemoteException;



          import java.rmi.RMISecurityManager;



          public class RemoteObject extends UnicastRemoteObject implements RemoteShiInterface

          {



                    String name;



                    public RemoteObject(String name)throws RemoteException



                    {



                              super();



                              this.name=name;



                    }



                    public String message(String message)throws RemoteException



                    {



                                      String returnString="My Name is :"+name+",thank for your message:"+message;



                              System.out.println("Returning:"+returnString);



                              System.out.println("hello stonewall";



          return "My Name is:"+name+",thanks for your message:"+message+"stonewall";



                    }



          public static void main(String args[])



                    {



                              //System.setSecurityManager(new RMISecurityManager());



                              try



                              {



                                        String myName="RMI";



                              RemoteObject theServer=new RemoteObject(myName);



                                        Naming.rebind("http://192.168.1.169:1099/RMI",theServer);



                                        System.out.println("Ready to continue";



                              }catch(Exception e)



                              {



                    System.out.println("An Exception occured while creating server";



                    System.out.println(e);



                              }



                    }



          }



           



          必須注意的就是運(yùn)用紅筆標(biāo)記出來(lái)的代碼。基本上就是按照此種樣式來(lái)編寫(xiě)的。另外,Naming rebind(“URL”,””)其中URL 必須制定,而且還要指定其訪問(wèn)端口如果不指定RMI默認(rèn)端口是1099。



          3.        編寫(xiě)使用遠(yuǎn)程對(duì)象



          package shi.rmi;



          import java.rmi.RMISecurityManager;



          import java.rmi.Naming;



          public class RemoteClient



          {



                    public static void main(String args[])



                    {



                              //System.setSecurityManager(new RMISecurityManager());



                              try



                              {



          RemoteShiInterface server=(RemoteShiInterface)Naming.lookup("http://192.168.1.169:1099/RMI";



                              String serverString=server.message("Hello There";



                              System.out.println("The server says:\n"+serverString);



                              }catch(Exception e)



                              {



                                        System.out.println(e);



                              }



                    }



          }



           



          System.setSecurityManager(new RMISecurityManager()) 安全管理的代碼,如果把它注釋掉,那么需要建立一個(gè)安全策略文件,比如文件名 policy.txt



          Grant {



               permission java.security.AllPermission “”,””;



          };



          運(yùn)行程序形式如下:



          D:\RMISample\server>java -Djava.security.policy=policy.txt RemoteObject



          D:\RMISample\client>java -Djava.security.policy=policy.txt RemoteClient



          如果注釋就可以直接運(yùn)行:



                                      Java RemoteObject     java RemoteClient



          4.        生成stub(客戶代理)以及skeletom(服務(wù)器實(shí)體)



          rmic RemoteObject



          5.        啟動(dòng)注冊(cè)表并且注冊(cè)對(duì)象



          start rmiregistry 1099



          6.        運(yùn)行服務(wù)器和客戶



          java RemoteObject



          java RemoteClient



           



           



           



           



               Socket



          Socket編程相對(duì)來(lái)說(shuō)就比較簡(jiǎn)單,服務(wù)器端利用ServerSocket的accept()方法來(lái)傾聽(tīng)客戶端發(fā)出的請(qǐng)求。如果,希望客戶端傾聽(tīng)網(wǎng)絡(luò)中多臺(tái)機(jī)器發(fā)出的請(qǐng)求,那么可以將Socket放到一個(gè)Thread中去。下面分別列出服務(wù)器端和客戶端代碼。



           



          服務(wù)器端代碼:



          ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Server.java



           



          package shi.socket;



          import java.io.*;



          import java.net.*;



           



          public class Server extends ServerSocket



          {



                 private static final int SERVER_PORT = 10000;



           



                 public Server() throws IOException



                 {



                        super(SERVER_PORT);



                        try{



                               while (true){



                                      Socket socket = accept();



                                      new CreateServerThread(socket);



                            }



                     }catch (IOException e){



                            e.printStackTrace();



                     }



                        finally{



                               close();



                        }



                 }



          }



           



          ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// CreateServerThread.java



          package shi.socket;



           



          import java.net.*;



          import java.io.*;



           



          public class CreateServerThread extends Thread



          {



                 private Socket client;



                 private BufferedReader in;



                 private PrintWriter out;



                 private static InetAddress serverAddr;



                 public CreateServerThread(Socket s) throws IOException{client = s;



                 in=new BufferedReader(new InputStreamReader(client.getInputStream(), "GB2312");



                        out = new PrintWriter(client.getOutputStream(), true);



                        out.println("--- Welcome ---";



                        start();



                 }



           



                 public void run(){   



                        try {



                              serverAddr =  InetAddress.getByName(null);



                              while (true) {  



                                String str = in.readLine();



                                if (str.equals("bye") break;



                                System.out.println("Echoing: " + str);



                                out.println(serverAddr + " : " + str);



                              }



                              out.println("---Good Bye---";



                              System.out.println("closing...";



                            } catch (IOException e) {



                            } finally {



                              try {



                                     client.close();



                              } catch(IOException e) {e.printStackTrace();}



                            }



                 }



                 public static void main(String args[]) throws IOException{



                 serverAddr =  InetAddress.getByName(null);



                 System.err.println(serverAddr.toString() +"--"+ "Create server Thread...";



                 new Server();



                 }



          }



           



          客戶端代碼:



          /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Client.java



          package shi.socket;



          import java.io.*;



          import java.net.*;



           



          public class Client



          {



                        Socket socket;



                        BufferedReader in;



                        PrintWriter out;



           



                        public Client()



                        {



                                      try



                                      {



                                                    InetAddress addr = 



                                                          InetAddress.getByName("192.168.1.169";



                                                    socket = new Socket(addr, 10000);



          in = new BufferedReader(new InputStreamReader(socket.getInputStream()));



                                                    out = new PrintWriter(socket.getOutputStream(),true);



                                      System.err.println(addr.toString() +"--"+ "Create Client Thread...";



                                                    System.out.println(in.readLine());



                                                    for (int i=0;i<10;i++){



                                                                  String str = addr.toString() + " : " + "The ";



                                                                  out.println(str + i);



                                                                  System.out.println(in.readLine());



                                                    }



                                                    out.println("bye";



                                                    System.out.println(in.readLine());



                                      }catch(Exception e){



                                                    e.printStackTrace();



                                      }finally{



                                                    System.out.println("closing...";



                                                    try {



                                                                  in.close();



                                                    } catch (IOException e1) {



                                                                  e1.printStackTrace();



                                                    }



                                                    out.close();



                                                    try {



                                                                  socket.close();



                                                    } catch (IOException e2) {



                                                                  e2.printStackTrace();



                                                    }



                                      }



                        }



           



                        public static void main(String[] args){



                                                    new Client();



                                      }



          }



           





           



                // wait for invocations from clients



                orb.run();



              } 



                 



                catch (Exception e) {



                  System.err.println("ERROR: " + e);



                  e.printStackTrace(System.out);



                }



                   



                System.out.println("HelloServer Exiting ...";



                 



            }



          }



          客戶端代碼:



           



          ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HelloClient.java



           



          import shi.corba.*;



          import org.omg.CosNaming.*;



          //import org.omg.CosNaming.NamingContextPackage.*;



          import org.omg.CORBA.*;



           



          public class HelloClient



          {



            static Hello helloImpl;



           



            public static void main(String args[])



              {



                try{



                  // create and initialize the ORB



                        ORB orb = ORB.init(args, null);



           



                  // get the root naming context



                  org.omg.CORBA.Object objRef = 



                            orb.resolve_initial_references("NameService";



                  // Use NamingContextExt instead of NamingContext. This is 



                  // part of the Interoperable naming Service.  



                  NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);



           



                  // resolve the Object Reference in Naming



                  String name = "Hello";



                  helloImpl = HelloHelper.narrow(ncRef.resolve_str(name));



           



                  System.out.println("Obtained a handle on server object: " + helloImpl);



                  System.out.println(helloImpl.sayHello());



                  helloImpl.shutdown();



           



                        } catch (Exception e) {



                    System.out.println("ERROR : " + e) ;



                          e.printStackTrace(System.out);



                          }



              }



           



          }



           



           



           



           



           



               綜述:



          分析以上三種技術(shù),我個(gè)人認(rèn)為CORBA適合大的網(wǎng)絡(luò)應(yīng)用。Socket適合于網(wǎng)絡(luò)間小數(shù)據(jù)量傳輸。RMI遠(yuǎn)程方法調(diào)用,不僅適合于網(wǎng)絡(luò)間的小型應(yīng)用,而且RMI的擴(kuò)展要好于Socket通信。



           

          2005-7-16
          關(guān)于COM Interop學(xué)習(xí)體會(huì)心得


          COM interop理論/實(shí)踐
          在.NET框架下,開(kāi)發(fā)人員可以通過(guò)COM interop tools 將COM組件導(dǎo)入導(dǎo)一個(gè)應(yīng)用中去,一旦導(dǎo)入成功,那么我么就可以非常容易地調(diào)用COM接口給我們所提供的方法了。
          A .NET Framework developer can incorporate COM components into a managed application by using COM interop tools to import the relevant COM types. Once imported, the COM types are ready to use.
          一、 COM interop 概述:
          COM Interop 看上去象是介乎于COM和.Net之間的一條紐帶,一座橋梁。為了保持向后兼容,COM Interop可以使得.Net程序在不修改原有COM組件的前提下方便的訪問(wèn)COM組件。這一點(diǎn)是非常重要的。事實(shí)上,全球的COM組件的代碼量估計(jì)可能有數(shù)十億行,擁有這些COM組件的公司不可能重寫(xiě)這些組件,所以COM Interop的存在為有此需求的開(kāi)發(fā)者提供了很好的解決方案。
          COM和.NET之間存在著非常大的差異,為了使兩者可以有機(jī)的結(jié)合在一起進(jìn)行協(xié)同工作,COM Interop中實(shí)際存在著2種橋接方式。一種是RCW,Runtime Callable Wrapper;另一種是 CCW,COM Callable Wrapper。RCW是在運(yùn)行時(shí)通過(guò)CLR從Interop裝配件(Interop Assembly)的元數(shù)據(jù)中獲取相關(guān)信息動(dòng)態(tài)的實(shí)例化而得到的。使用者將感覺(jué)不到自己是在調(diào)用COM組件,一切都是這么的自然,和調(diào)用一個(gè).Net組件沒(méi)有任何區(qū)別。  
          需要注意的是,一個(gè)COM組件(指的是一個(gè)實(shí)例,即一個(gè)DLL文件)由且僅由一個(gè)RCW負(fù)責(zé)維護(hù)。那么這兒有一個(gè)問(wèn)題了,對(duì)于一個(gè)COM組件的不同版本,是不是就會(huì)有不同的RCW與之相對(duì)應(yīng)呢?答案是肯定的。那有些朋友會(huì)說(shuō),.Net中的組件不是已經(jīng)解決了COM中的“DLL HELL”問(wèn)題了嗎?按上面的說(shuō)法,似乎并沒(méi)有得到解決嘛?這兒我要說(shuō)的是,在.Net中導(dǎo)入一個(gè)COM組件的不同版本,是會(huì)出現(xiàn)此類問(wèn)題。解決此類問(wèn)題的方法是使用PIA (Primary Interop Assembly)。
          .Net提供三種方法來(lái)導(dǎo)入一個(gè)COM組件
          l 通過(guò)Visual Studio .Net提從的“添加引用”功能
          l 通過(guò)tlbimp.exe實(shí)現(xiàn)
          l 使用System.Runtime.InteropServices.TypeLibConverter類編程
          下面就分別介紹COM的封裝、HRESULTs and Exceptions、繼承、聚合和包容、如何運(yùn)用COM interop來(lái)生成發(fā)出事件和處理事件以及System.Runtime.InteropServices命名空間幾個(gè)重要的概念
          1. COM的封裝(COM Wrappers)
          l 在一般的語(yǔ)言(諸如C++)當(dāng)中在客戶端我們一定要控制該COM對(duì)象的生命周期
          l 客戶端的COM對(duì)象的方法在C++中的調(diào)用通過(guò)產(chǎn)生該對(duì)象的實(shí)例同時(shí)獲得該對(duì)象的接口指針,通過(guò)接口指針來(lái)訪問(wèn)該對(duì)象的方法。在.NET框架下則可以直接通過(guò)函數(shù)的映射來(lái)獲得 (Clients of .NET objects can obtain a description of an object's functionality using Reflection.)
          l 在.NET框架下的運(yùn)行環(huán)境中.NET可以在內(nèi)存中為.NET重新對(duì)象分配內(nèi)存使用。(NET objects reside in memory managed by the .NET Framework execution environment.)
          為了解決上述問(wèn)題,.NET提供一個(gè)COM Wrappers.它可以使得Managed Code 和 Unmanaged Code可以很好結(jié)合在一起。COM Wrappers兩種橋接方式RCW(runtime callable wrapper)和CCW (COM callable wrapper)其中RCW是將Managed的客戶端與Unmanaged的服務(wù)器端聯(lián)接起來(lái)的;CCW是將 Unmanaged的客戶端與 Managed的服務(wù)器端聯(lián)接起來(lái)的。
          2. HRESULTs and Exceptions
          在COM編程中我們通過(guò)HRESULT來(lái)判斷所做的操作是否成功,在.NET框架下我們通過(guò)拋不同的異常(Throw Exceptions)來(lái)捕捉錯(cuò)誤。
          注MSDN給我們列出了HRESULT不同值與.NET的不同異常的對(duì)照表。(.NET Framework Developer’s Guide—HRESULTs and Exceptions)
          3. 繼承、聚合和包容(Inheritance,Aggregation and Containment)
          繼承:.NET提供一些標(biāo)準(zhǔn)的接口,用戶在定義接口時(shí),可以繼承這些接口。
          聚合:.NET也支持COM提供的聚合的概念即,外對(duì)象將內(nèi)對(duì)象的接口暴露在用戶面前。
          包容:
              通過(guò)在外對(duì)象的構(gòu)造函數(shù)中創(chuàng)建內(nèi)對(duì)象的實(shí)例,這樣客戶端就可以通過(guò)該實(shí)例獲得接口進(jìn)行調(diào)用接口的各個(gè)方法。
          4. 如何運(yùn)用COM interop來(lái)生成發(fā)出事件和處理事件
          在以下內(nèi)容中,將要描述有關(guān)COM對(duì)象出接口與事件接收器的連接機(jī)制。
          關(guān)于COM對(duì)象的出接口與事件接收器之間的連接機(jī)制與在描述COM原理與應(yīng)用中的機(jī)制是一樣的,即COM對(duì)象聲明一個(gè)出接口,在事件接收器中表示該接口的實(shí)現(xiàn)方法。一旦,COM對(duì)象與事件接收器的連接建立好以后,那么客戶端就可以隨時(shí)接收到COM對(duì)象服務(wù)器端的事件、消息。下面我們從C#的服務(wù)器端和事件接收器兩個(gè)方面來(lái)描述這個(gè)問(wèn)題。
          Handling Events Raised by a COM Source(描述COM源對(duì)象是如何產(chǎn)生一個(gè)事件的)
          Raising Events Handled by a COM Sink(通過(guò)接收器來(lái)處理事件)
          5. System.Runtime.InteropServices命名空間
          System.Runtime.InteropServices是一個(gè)有關(guān)訪問(wèn)COM對(duì)象以及在.NET框架下的本地API函數(shù)。在創(chuàng)建COM接口時(shí)經(jīng)常要運(yùn)用這個(gè)命名空間。
          二、 C#接口編程
               下面將從接口的定義、接口的訪問(wèn)、接口的實(shí)現(xiàn)以及接口的轉(zhuǎn)換編程這些方面來(lái)闡述運(yùn)用C#進(jìn)行接口編程的方法。
          1. 接口的定義
          接口的聲明:
          [attributes] [modifiers] interface identifier [:base-list] {interface-body}[;]
          l attributes(可選):附加的定義性信息。
          l · modifiers(可選):允許使用的修飾符有new和四個(gè)訪問(wèn)修飾符。分別是:new、public、protected、internal、 private。在一個(gè)接口定義中同一修飾符不允許出現(xiàn)多次,new修飾符只能出現(xiàn)在嵌套接口中,表示覆蓋了繼承而來(lái)的同名成員。The public,  protected, internal, and private修飾符定義了對(duì)接口的訪問(wèn)權(quán)限。 
          l 指示器和事件。
          l identifier:接口名稱。 
          l base-list(可選):包含一個(gè)或多個(gè)顯式基接口的列表,接口間由逗號(hào)分隔。 
          l interface-body:對(duì)接口成員的定義。 
          l 接口可以是命名空間或類的成員,并且可以包含下列成員的簽名: 方法、屬性、索引器 。 
          l 一個(gè)接口可從一個(gè)或多個(gè)基接口繼承。
          接口的主體:
          interface-body:  {   interface-member-declarationsopt   }
          接口可以包含一個(gè)和多個(gè)成員,這些成員可以是方法、屬性、索引指示器和事件,但不能是常量、域、操作符、構(gòu)造函數(shù)或析構(gòu)函數(shù),而且不能包含任何靜態(tài)成員。接口定義創(chuàng)建新的定義空間,并且接口定義直接包含的接口成員定義將新成員引入該定義空間。
             2. 接口的訪問(wèn)
          C#中的CASTS來(lái)代替QueryInterface
          (Using Casts Instead of QueryInterface)
          在C++中客戶端需要通過(guò)QueryInterface來(lái)獲得COM對(duì)象的接口指針。在C#編程中卻不必這么麻煩。我們可以直接將COM對(duì)象對(duì)應(yīng)到相應(yīng)的COM接口上。如果我們?cè)诔绦蛑袑?duì)應(yīng)錯(cuò)誤,那么在運(yùn)行時(shí)C#會(huì)拋出異常。
             3. 接口的實(shí)現(xiàn)
          顯示地實(shí)現(xiàn)接口成員即可以直接利用類來(lái)實(shí)現(xiàn)接口的成員函數(shù)如:
              // Since the .NET Framework interface and coclass have to behave as 
              // COM objects, we have to give them guids.
              [Guid("DBE0E8C4-1C61-41f3-B6A4-4E2F353D3D05"]
              public interface IManagedInterface
              {
                 int PrintHi(string name);
              }
              [Guid("C6659361-1625-4746-931C-36014B146679"]
              public class InterfaceImplementation : IManagedInterface
              {
                 public int PrintHi(string name)
                 {
                    Console.WriteLine("Hello, {0}!", name);
                     return 33;
                    }
              }
          通過(guò)為Coclass的方法來(lái)實(shí)現(xiàn)接口如:
          [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"
                  InterfaceType(ComInterfaceType.InterfaceIsDual)] 
              interface IMediaControl   // Cannot list any base interfaces here 
              { // COM methods     }
          // Declare FilgraphManager as a COM coclass:
               [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770"
               class FilgraphManager   // Cannot have a base class or
                                           // interface list here.
               { 
                   // Cannot have any members here 
                   // NOTE that the C# compiler will add a default constructor
                   // for you (no parameters).
             }
          其中類FilgraphManager實(shí)現(xiàn)了ImediaControl的方法其中ComImport--它將類標(biāo)記為在外部實(shí)現(xiàn)的 COM 類。
             4. 接口的轉(zhuǎn)換編程
          運(yùn)用C#開(kāi)發(fā)COM組件服務(wù)器端
          l 聲明一個(gè)COM接口(Declaring a COM Interface):
          首先聲明一個(gè)COM接口我們必須要明確該接口是繼承IUnknown or IDispatch或者其他接口。運(yùn)用C#描述這方面內(nèi)容必須使用C#提供的屬性InterfaceType(InterfaceType – 表明接口是繼承于 IUnknown or IDispatch)
          其次,為了使COM接口中出現(xiàn)成員函數(shù),程序中也要指出ComImport and Guid屬性。(Guid—表明接口或者coclass的唯一標(biāo)示號(hào);ComImport—它將類標(biāo)記為在外部實(shí)現(xiàn)的 COM 類。)
          注:coclass是(簡(jiǎn)稱組件對(duì)象類――component object class)被包含在DLL或EXE中,并且包含著一個(gè)或者多個(gè)接口的代碼。客戶端通過(guò)創(chuàng)建該對(duì)象的實(shí)例來(lái)獲得COM對(duì)象的接口。
          例子代碼:
          [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"
                  InterfaceType(ComInterfaceType.InterfaceIsDual)] 
              interface IMediaControl   // Cannot list any base interfaces here 
              {
          // COM methods
               }
          l 聲明一個(gè)組件對(duì)象類(Declaring a COM coclass)
          1. 該類不可以繼承于其他類。
          2. 也不可以實(shí)現(xiàn)任何接口。
          3. 必須有一個(gè)Guid來(lái)唯一標(biāo)示該類
          例子代碼:
          // Declare FilgraphManager as a COM coclass:
               [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770"
          //"E436EBB3-524F-11CE-9F53-0020AF0BA770"標(biāo)示號(hào)在系統(tǒng)注冊(cè)表中可以找到,說(shuō)明//此處是調(diào)用外部的COM類來(lái)實(shí)現(xiàn)接口ImediaControl的。
               class FilgraphManager   // Cannot have a base class or
                                           // interface list here.
               { 
                   // Cannot have any members here 
                   // NOTE that the C# compiler will add a default constructor
                   // for you (no parameters).
            }
          運(yùn)用C#開(kāi)發(fā)COM組件客戶端
          l C#中的CASTS來(lái)代替QueryInterface
          (Using Casts Instead of QueryInterface)
          在C++中客戶端需要通過(guò)QueryInterface來(lái)獲得COM對(duì)象的接口指針。在C#編程中卻不必這么麻煩。我們可以直接將COM對(duì)象對(duì)應(yīng)到相應(yīng)的COM接口上。如果我們?cè)诔绦蛑袑?duì)應(yīng)錯(cuò)誤,那么在運(yùn)行時(shí)C#會(huì)拋出異常。
          例子代碼:
          // Create an instance of a COM coclass:
          FilgraphManager graphManager = new FilgraphManager();
          // See if it supports the IMediaControl COM interface. 
          // Note that this will throw a System.InvalidCastException if 
          // the cast fails. This is equivalent to QueryInterface for 
          // COM objects:
          IMediaControl mc = (IMediaControl) graphManager;
          // Now you call a method on a COM interface: 
          mc.Run();
          l C#運(yùn)用拋異常的機(jī)制來(lái)代替HRESULT
          在創(chuàng)建COM對(duì)象實(shí)例以及獲得接口時(shí)可能都會(huì)出現(xiàn)問(wèn)題,在C++中我們通過(guò)調(diào)用HRESULT來(lái)判斷所做的操作是否成功。但是,在C#編程中我們就不必那么麻煩,C#會(huì)拋出諸如System.COMException的異常。
          三、關(guān)于C#中幾個(gè)工具的用法
          1. Tlbimp.exe的用法:
          該工具適合將*.tlb的類庫(kù)文件可以轉(zhuǎn)換成*.dll的文件,同時(shí)該工具還可以將一個(gè)dll輸出到一個(gè)新的dll文件當(dāng)中去。
          例:tlbimp myTest.tlb
          輸出 MYTESTLIB.dll的動(dòng)態(tài)聯(lián)接庫(kù)文件

          tlbimp  myTest.tlb  /out:myTest.dll
          輸出生成 myTest.dll的文件
          tlbimp c:\winnt\system32\quartz.dll /out:QuartzTypeLib.dll
          將quartz.dll包含到QuartzTypeLib.dll中去。
          2. Ildasm工具可以查看*.dll, *.exe, .obj, .lib 文件
          例如可以查看.dll的具體情況比如接口、實(shí)現(xiàn)類以及成員函數(shù)等信息。
          如:可以通過(guò)命令I(lǐng)ldasm *.dll來(lái)查看具體生成的dll的情況。
          3. Regasm工具用于將dll動(dòng)態(tài)聯(lián)接庫(kù)注冊(cè)到注冊(cè)表中的操作
          如:Regasm QuartzTypeLib.dll
          就將QuartzTypeLib.dll注冊(cè)到注冊(cè)表中去。
          如果要解除剛才的注冊(cè)操作可以運(yùn)用
          Regasm /unregister QuartzTypeLib.dll命令即可。
          4. CSC工具的使用是編譯C#文件*.cs為exe或者dll文件的工具
          如:csc File.cs 就是將File.cs編譯為File.exe文件
          csc /out:my.exe File.cs將File.cs文件編譯為my.exe
          csc /target:library File.cs就是將File.cs編譯為File.dll文件
          5.guidgen工具用于生成Guid號(hào)
          在C#中使用工具
          $\Microsoft Visual Studio .NET 2003\Common7\Tools\$下的工具
          guidgen.exe生成一個(gè)隨機(jī)的GUID號(hào)。
          三、 運(yùn)用C#開(kāi)發(fā)COM的組件的簡(jiǎn)單例子
          功能概述此例子非常簡(jiǎn)單就是在服務(wù)器端寫(xiě)一個(gè)具有現(xiàn)實(shí)Hello, **的方法
          客戶端通過(guò)調(diào)用服務(wù)器端的PrintHi(String name)函數(shù)顯示出 Hello, **的信息。
          服務(wù)器端代碼:
          //Copyright (C) 2000 Microsoft Corporation.  All rights reserved.
          // ShiServer.cs
          // compile with: /target:library
          using System;
          using System.Runtime.InteropServices;
          namespace ShiServer
          {
          // Since the .NET Framework interface and coclass have to behave as 
          // COM objects, we have to give them guids.
          [Guid("976F8704-6E29-4b67-AC4F-A5B1226D1F49"]
          public interface IshiInterface
          {
          int PrintHi(string name);
          }
          [Guid("2BDD9B83-E4FC-432a-BBE1-F71C05723AB6"]
          public class IshiImplementation : IshiInterface
          {
          public int PrintHi(string name)
          {
          Console.WriteLine("Hello, {0}!", name);
          return 33;
          }
          }
          }
          客戶端代碼:
          //Copyright (C) 2000 Microsoft Corporation.  All rights reserved.
          // shi.cs
          // Build with "csc /R:ShiServer.dll shi.cs"
          using System;
          class MainClass 

              /************************************************************ 
          show Hello,sname
              **************************************************************/ 
              public static void Main(string[] args) 
              { 
                  // input a name
          string sname = args[0];
                  // Create instance of ShiServer
                  // (Calls CoCreateInstance(D3E09FD9-B987-47f3-89CB-95EFB4D68583,
                  // NULL, CLSCTX_ALL, IID_IUnknown, &myHello).
                  // Returns null on failure):         
                  ShiServer.IshiImplementation myHello=
                        new ShiServer.IshiImplementation();
                  // QueryInterface for the IshiInterface interface:
                  ShiServer.IshiInterface myIhello=
                      (ShiServer.IshiInterface)myHello;
                        
                  // Call some methods on a COM interface 
          myIhello.PrintHi(sname);
              }
          }
          輸入: shi stonewall   輸出:Hello, stonewall

          注:以上資料均來(lái)自于
          MSDN Library –January 2002 
          TiTle: C# Programmer’s Reference   COM Interop Tutorials
          COM interop 概述 ::URL::http://www.xmlasp.net/n966c13.aspx
          What’s new for Interop in .NET Framework v2.0?       
          ::URL::http://www.dotnetinterop.com/features/default.aspx?q=Whidbey
          C#COM接口編程
          ::URL::http://tech.ccidnet.com/pub/disp/Article?columnID=295&articleID=40725&pageNO=1

          posted on 2007-04-27 09:51 天外飛仙 閱讀(2394) 評(píng)論(1)  編輯  收藏 所屬分類: Oracle

          Feedback

          # re: 研究Oracle 2008-11-21 12:29 過(guò)路人
          好長(zhǎng)  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 荣昌县| 庄河市| 珠海市| 体育| 辽宁省| 门源| 湟源县| 辉县市| 江孜县| 宝山区| 大埔县| 铁力市| 濮阳县| 隆安县| 昌都县| 宽甸| 视频| 札达县| 平遥县| 南投县| 论坛| 阿荣旗| 定边县| 柳州市| 丰城市| 双辽市| 张北县| 纳雍县| 临安市| 筠连县| 衢州市| 武隆县| 井陉县| 阳春市| 都兰县| 台安县| 嵊州市| 宁远县| 金堂县| 景泰县| 颍上县|