<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆:93 文章:11 評論:22 引用:0
    首頁 發新隨筆
    發新文章 聯系 聚合管理

    摘自:
    http://shitou521.iteye.com/blog/696006

    JNDI的一篇文章

    前端時間總是在搞不清JNDI到底是干什么,雖然是一值在用,卻不知道他最初出現的原因,用來,說不清是用來干什么,下面我相信介能解開這個迷霧里。

    轉貼一篇】 
    ------------ 
    JNDI是 Java 命名與目錄接口(Java Naming and Directory Interface),在J2EE規范中是重要的規范之一,不少專家認為,沒有透徹理解JNDI的意義和作用,就沒有真正掌握J2EE特別是EJB的知識。 

    那么,JNDI到底起什么作用?//帶著問題看文章是最有效的 

    要了解JNDI的作用,我們可以從“如果不用JNDI我們怎樣做?用了JNDI后我們又將怎樣做?”這個問題來探討。 

    沒有JNDI的做法: 

    程序員開發時,知道要開發訪問MySQL數據庫的應用,于是將一個對 MySQL JDBC 驅動程序類的引用進行了編碼,并通過使用適當的 JDBC URL 連接到數據庫。 
    就像以下代碼這樣: 

    Java代碼 
    1. Connection conn=null;  
    2. try {  
    3.   Class.forName("com.mysql.jdbc.Driver",  
    4.                 true, Thread.currentThread().getContextClassLoader());  
    5.   conn=DriverManager.  
    6.     getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");  
    7.   ......  
    8.   conn.close();  
    9. catch(Exception e) {  
    10.   e.printStackTrace();  
    11. finally {  
    12.   if(conn!=null) {  
    13.     try {  
    14.       conn.close();  
    15.     } catch(SQLException e) {}  
    16.   }  
    17. }  



    這是傳統的做法,也是以前非Java程序員(如Delphi、VB等)常見的做法。這種做法一般在小規模的開發過程中不會產生問題,只要程序員熟悉Java語言、了解JDBC技術和MySQL,可以很快開發出相應的應用程序。 

    沒有JNDI的做法存在的問題: 
    1、數據庫服務器名稱MyDBServer 、用戶名和口令都可能需要改變,由此引發JDBC URL需要修改; 
    2、數據庫可能改用別的產品,如改用DB2或者Oracle,引發JDBC驅動程序包和類名需要修改; 
    3、隨著實際使用終端的增加,原配置的連接池參數可能需要調整; 
    4、...... 

    解決辦法: 
    程 序員應該不需要關心“具體的數據庫后臺是什么?JDBC驅動程序是什么?JDBC URL格式是什么?訪問數據庫的用戶名和口令是什么?”等等這些問題,程序員編寫的程序應該沒有對 JDBC 驅動程序的引用,沒有服務器名稱,沒有用戶名稱或口令 —— 甚至沒有數據庫池或連接管理。而是把這些問題交給J2EE容器來配置和管理,程序員只需要對這些配置和管理進行引用即可。 

    由此,就有了JNDI。 
    //看的出來,是為了一個最最核心的問題:是為了解耦,是為了開發出更加可維護、可擴展//的系統 

    用了JNDI之后的做法: 
    首先,在在J2EE容器中配置JNDI參數,定義一個數據源,也就是JDBC引用參數,給這個數據源設置一個名稱;然后,在程序中,通過數據源名稱引用數據源從而訪問后臺數據庫。 

    //紅色的字可以看出,JNDI是由j2ee容器提供的功能 

    具體操作如下(以JBoss為例): 
    1、配置數據源 
    在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夾下面,有很多不同數據庫引用的數據源定義模板。將其中的 mysql-ds.xml 文件Copy到你使用的服務器下,如 D:\jboss420GA\server\default\deploy。 
    修改 mysql-ds.xml 文件的內容,使之能通過JDBC正確訪問你的MySQL數據庫,如下: 
    Java代碼 
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <datasources>  
    3. <local-tx-datasource>  
    4.     <jndi-name>MySqlDS</jndi-name>  
    5.     <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>  
    6.     <driver-class>com.mysql.jdbc.Driver</driver-class>  
    7.     <user-name>root</user-name>  
    8.     <password>rootpassword</password>  
    9. <exception-sorter-class-name>  
    10. org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter  
    11. </exception-sorter-class-name>  
    12.     <metadata>  
    13.        <type-mapping>mySQL</type-mapping>  
    14.     </metadata>  
    15. </local-tx-datasource>  
    16. </datasources>  


    這里,定義了一個名為MySqlDS的數據源,其參數包括JDBC的URL,驅動類名,用戶名及密碼等。 

    2、在程序中引用數據源: 

    Java代碼 
    1. Connection conn=null;  
    2. try {  
    3.   Context ctx=new InitialContext();  
    4.   Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用數據源  
    5.   DataSource ds=(Datasource)datasourceRef;  
    6.   conn=ds.getConnection();  
    7.   ......  
    8.   c.close();  
    9. catch(Exception e) {  
    10.   e.printStackTrace();  
    11. finally {  
    12.   if(conn!=null) {  
    13.     try {  
    14.       conn.close();  
    15.     } catch(SQLException e) { }  
    16.   }  
    17. }  


    直接使用JDBC或者通過JNDI引用數據源的編程代碼量相差無幾,但是現在的程序可以不用關心具體JDBC參數了。//解藕了,可擴展了 
    在系統部署后,如果數據庫的相關參數變更,只需要重新配置 mysql-ds.xml 修改其中的JDBC參數,只要保證數據源的名稱不變,那么程序源代碼就無需修改。 

    由此可見,JNDI避免了程序與數據庫之間的緊耦合,使應用更加易于配置、易于部署。 

    JNDI的擴展: 
    JNDI在滿足了數據源配置的要求的基礎上,還進一步擴充了作用:所有與系統外部的資源的引用,都可以通過JNDI定義和引用。 
    //注意什么叫資源 

    所以,在J2EE規范中,J2EE 中的資源并不局限于 JDBC 數據源。引用的類型有很多,其中包括資源引用(已經討論過)、環境實體和 EJB 引用。特別是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一項關鍵角色:查找其他應用程序組件。 

    EJB 的 JNDI 引用非常類似于 JDBC 資源的引用。在服務趨于轉換的環境中,這是一種很有效的方法。可以對應用程序架構中所得到的所有組件進行這類配置管理,從 EJB 組件到 JMS 隊列和主題,再到簡單配置字符串或其他對象,這可以降低隨時間的推移服務變更所產生的維護成本,同時還可以簡化部署,減少集成工作。外部資源”。 


    總結: 
    J2EE 規范要求所有 J2EE 容器都要提供 JNDI 規范的實現。//sun 果然喜歡制定規范JNDI 在 J2EE 中的角色就是“交換機” —— J2EE 組件在運行時間接地查找其他組件、資源或服務的通用機制。在多數情況下,提供 JNDI 供應者的容器可以充當有限的數據存儲,這樣管理員就可以設置應用程序的執行屬性,并讓其他應用程序引用這些屬性(Java 管理擴展(Java Management Extensions,JMX)也可以用作這個目的)。JNDI 在 J2EE 應用程序中的主要角色就是提供間接層,這樣組件就可以發現所需要的資源,而不用了解這些間接性。 

    在 J2EE 中,JNDI 是把 J2EE 應用程序合在一起的粘合劑,JNDI 提供的間接尋址允許跨企業交付可伸縮的、功能強大且很靈活的應用程序。這是 J2EE 的承諾,而且經過一些計劃和預先考慮,這個承諾是完全可以實現的。 


    從上面的文章中可以看出: 
    1、JNDI 提出的目的是為了解藕,是為了開發更加容易維護,容易擴展,容易部署的應用。 
    2、JNDI 是一個sun提出的一個規范(類似于jdbc),具體的實現是各個j2ee容器提供商,sun   只是要求,j2ee容器必須有JNDI這樣的功能。 
    3、JNDI 在j2ee系統中的角色是“交換機”,是J2EE組件在運行時間接地查找其他組件、資源或服務的通用機制。 
    4、JNDI 是通過資源的名字來查找的,資源的名字在整個j2ee應用中(j2ee容器中)是唯一的。 

    再轉一篇文章: 


    JNDI全稱 Java Naming and Directory Interface 
    JNDI 是Java平臺的一個標準擴展,提供了一組接口、類和關于命名空間的概念。如同其它很多Java技術一樣,JDNI是provider-based的技 術,暴露了一個API和一個服務供應接口(SPI)。這意味著任何基于名字的技術都能通過JNDI而提供服務,只要JNDI支持這項技術。JNDI目前所 支持的技術包括LDAP、CORBA Common Object Service(COS)名字服務、RMI、NDS、DNS、Windows注冊表等等。很多J2EE技術,包括EJB都依靠JNDI來組織和定位實體。 
    JDNI通過綁定的概念將對象和名稱聯系起來。在一個文件系統中,文件名被綁定給文件。在DNS中,一個IP地址綁定一個URL。在目錄服務中,一個對象名被綁定給一個對象實體。 
    JNDI 中的一組綁定作為上下文來引用。每個上下文暴露的一組操作是一致的。例如,每個上下文提供了一個查找操作,返回指定名字的相應對象。每個上下文都提供了綁 定和撤除綁定名字到某個對象的操作。JNDI使用通用的方式來暴露命名空間,即使用分層上下文以及使用相同命名語法的子上下文。 
    jndi的用途: 
    1。你可以用jndi來得到object類的屬性 
    如: 
    Java代碼 
    1. Attribute attr =directory.getAttributes(personName).get("email");   
    2. String email = (String)attr.get();   

    2。你可以用jndi來搜索對象 
    如: 
    Java代碼 
    1. foxes = directory.search("o=Wiz,c=US""sn=Fox", controls);   

    查找誰的名字叫Fox在wiz部門的員工? 
    3。你可以用jndi通過naming/directory服務查詢像printers和databases的對象 
    如:查詢 Printer 
    Java代碼 
    1. Printer printer = (Printer)namespace.lookup(printerName);   
    2. printer.print(document);   

    4。你可以用jndi列表出命名空間的特殊級別的內容 
    如: 
    Java代碼 
    1. NamingEnumeration list = namespace.list("o=Widget, c=US";   
    2. while (list.hasMore()) {   
    3. NameClassPair entry = (NameClassPair)list.next();   
    4. display(entry.getName(), entry.getClassName());   
    5. }  
    posted @ 2011-05-22 10:34 redcoatjk 閱讀(416) | 評論 (0)編輯 收藏
     
    各種數字類型轉換成字符串型:

    String s = String.valueOf( value); // 其中 value 為任意一種數字類型。

    字符串型轉換成各種數字類型:

    String s = "169";
    byte b = Byte.parseByte( s );
    short t = Short.parseShort( s );
    int i = Integer.parseInt( s );
    long l = Long.parseLong( s );
    Float f = Float.parseFloat( s );
    Double d = Double.parseDouble( s );

    數字類型與數字類對象之間的轉換:

    byte b = 169;
    Byte bo = new Byte( b );
    b = bo.byteValue();

    short t = 169;
    Short to = new Short( t );
    t = to.shortValue();

    int i = 169;
    b = bo.byteValue();

    short t = 169;
    Short to = new Short( t );
    t = to.shortValue();

    int i = 169;
    Integer io = new Integer( i );
    i = io.intValue();

    long l = 169;
    Long lo = new Long( l );
    l = lo.longValue();

    float f = 169f;
    Float fo = new Float( f );
    f = fo.floatValue();

    double d = 169f;
    Double dObj = new Double( d );
    d = dObj.doubleValue();
    posted @ 2011-02-21 13:34 redcoatjk 閱讀(205) | 評論 (0)編輯 收藏
     
    1. 觸發器使用教程和命名規范  
    2.   
    3.   
    4. 目  錄  
    5. 觸發器使用教程和命名規范    1  
    6. 1,觸發器簡介 1  
    7. 2,觸發器示例 2  
    8. 3,觸發器語法和功能  3  
    9. 4,例一:行級觸發器之一    4  
    10. 5,例二:行級觸發器之二    4  
    11. 6,例三:INSTEAD OF觸發器  6  
    12. 7,例四:語句級觸發器之一   8  
    13. 8,例五:語句級觸發器之二   9  
    14. 9,例六:用包封裝觸發器代碼  10  
    15. 10,觸發器命名規范  11  
    16.   
    17. 1,觸發器簡介  
    18. 觸發器(Trigger)是數據庫對象的一種,編碼方式類似存儲過程,與某張表(Table)相關聯,當有DML語句對表進行操作時,可以引起觸發器的執行,達到對插入記錄一致性,正確性和規范性控制的目的。在當年C/S時代盛行的時候,由于客戶端直接連接數據庫,能保證數據庫一致性的只有數據庫本身,此時主鍵(Primary Key),外鍵(Foreign Key),約束(Constraint)和觸發器成為必要的控制機制。而觸發器的實現比較靈活,可編程性強,自然成為了最流行的控制機制。到了B/S時代,發展成4層架構,客戶端不再能直接訪問數據庫,只有中間件才可以訪問數據庫。要控制數據庫的一致性,既可以在中間件里控制,也可以在數據庫端控制。很多的青睞Java的開發者,隨之將數據庫當成一個黑盒,把大多數的數據控制工作放在了Servlet中執行。這樣做,不需要了解太多的數據庫知識,也減少了數據庫編程的復雜性,但同時增加了Servlet編程的工作量。從架構設計來看,中間件的功能是檢查業務正確性和執行業務邏輯,如果把數據的一致性檢查放到中間件去做,需要在所有涉及到數據寫入的地方進行數據一致性檢查。由于數據庫訪問相對于中間件來說是遠程調用,要編寫統一的數據一致性檢查代碼并非易事,一般采用在多個地方的增加類似的檢查步驟。一旦一致性檢查過程發生調整,勢必導致多個地方的修改,不僅增加工作量,而且無法保證每個檢查步驟的正確性。觸發器的應用,應該放在關鍵的,多方發起的,高頻訪問的數據表上,過多使用觸發器,會增加數據庫負擔,降低數據庫性能。而放棄使用觸發器,則會導致系統架構設計上的問題,影響系統的穩定性。  
    19.   
    20.   
    21. 2,觸發器示例  
    22. 觸發器代碼類似存儲過程,以PL/SQL腳本編寫。下面是一個觸發器的示例:  
    23. 新建員工工資表salary  
    24. create table SALARY  
    25. (  
    26.   EMPLOYEE_ID NUMBER, --員工ID  
    27.   MONTH       VARCHAR2(6), --工資月份  
    28.   AMOUNT      NUMBER --工資金額  
    29. )  
    30.   
    31. 創建與salary關聯的觸發器salary_trg_rai  
    32. 1   Create or replace trigger salary_trg_rai  
    33. 2   After insert on salary  
    34. 3   For each row  
    35. 4   declare  
    36. 5   Begin  
    37. 6     Dbms_output.put_line(‘員工ID:’ || :new.employee_id);  
    38. 7     Dbms_output.put_line(‘工資月份:’ || :new.month);  
    39. 8     Dbms_output.put_line(‘工資:’ || :new.amount);  
    40. 9     Dbms_output.put_line(‘觸發器已被執行’);  
    41. 10   End;  
    42. 打開一個SQL Window窗口(使用PL/SQL Developer工具),或在sqlplus中輸入:  
    43. Insert into salary(employee_id, month, amount) values(1, ‘200606’, 10000);  
    44. 執行后可以在sqlplus中,或在SQL Window窗口的Output中見到  
    45. 員工ID:1  
    46. 工資月份:200606  
    47. 工資:10000  
    48. 觸發器已執行  
    49.   
    50. 在代碼的第一行,定義了數據庫對象的類型是trigger,定義觸發器的名稱是salary_trg_rai  
    51. 第二行說明了這是一個after觸發器,在DML操作實施之后執行。緊接著的insert說明了這是一個針對insert操作的觸發器,每個對該表進行的insert操作都會執行這個觸發器。  
    52. 第三行說明了這是一個針對行級的觸發器,當插入的記錄有n條時,在每一條插入操作時都會執行該觸發器,總共執行n次。  
    53. Declare后面跟的是本地變量定義部分,如果沒有本地變量定義,此部分可以為空  
    54. Begin和end括起來的代碼,是觸發器的執行部分,一般會對插入記錄進行一致性檢查,在本例中打印了插入的記錄和“觸發器已執行”。  
    55. 其中:new對象表示了插入的記錄,可以通過:new.column_name來引用記錄的每個字段值  
    56.   
    57.   
    58. 3,觸發器語法和功能  
    59. 觸發器的語法如下  
    60. CREATE OR REPLACE TRIGGER trigger_name  
    61. <before | after | instead of> <insert | update | delete> ON table_name  
    62. [FOR EACH ROW]  
    63. WHEN (condition)  
    64. DECLARE  
    65. BEGIN  
    66.     --觸發器代碼  
    67. END;  
    68.   
    69. Trigger_name 是觸發器的名稱。<before | after | instead of>可以選擇before或者after或instead of。 Before表示在DML語句實施前執行觸發器,而after表示在在dml語句實施之后執行觸發器,instead of觸發器用在對視圖的更新上。<insert | update | delete>可以選擇一個或多個DML語句,如果選擇多個,則用or分開,如:insert or update。Table_name是觸發器關聯的表名。  
    70. [FOR EACH ROW]為可選項,如果注明了FOR EACH ROW,則說明了該觸發器是一個行級的觸發器,DML語句處理每條記錄都會執行觸發器;否則是一個語句級的觸發器,每個DML語句觸發一次。  
    71. WHEN后跟的condition是觸發器的響應條件,只對行級觸發器有效,當操作的記錄滿足condition時,觸發器才被執行,否則不執行。Condition中可以通過new對象和old對象(注意區別于前面的:new和:old,在代碼中引用需要加上冒號)來引用操作的記錄。  
    72. 觸發器代碼可以包括三種類型:未涉及數據庫事務代碼,涉及關聯表(上文語法中的table_name)數據庫事務代碼,涉及除關聯表之外數據庫事務代碼。其中第一種類型代碼只對數據進行簡單運算和判斷,沒有DML語句,這種類型代碼可以在所有的觸發器中執行。第二種類型代碼涉及到對關聯表的數據操作,比如查詢關聯表的總記錄數或者往關聯表中插入一條記錄,該類型代碼只能在語句級觸發器中使用,如果在行級觸發器中使用,將會報ORA-04091錯誤。第三種類型代碼涉及到除關聯表之外的數據庫事務,這種代碼可以在所有觸發器中使用。  
    73.   
    74. 從觸發器的功能上來看,可以分成3類:  
    75. ?   重寫列(僅限于before觸發器)  
    76. ?   采取行動(任何觸發器)  
    77. ?   拒絕事務(任何觸發器)  
    78. “重寫列”用于對表字段的校驗,當插入值為空或者插入值不符合要求,則觸發器用缺省值或另外的值代替,在多數情況下與字段的default屬性相同。這種功能只能在行級before觸發器中執行。“采取行動”針對當前事務的特點,對相關表進行操作,比如根據當前表插入的記錄更新其他表,銀行中的總帳和分戶帳間的總分關系就可以通過這種觸發器功能來維護。“拒絕事務”用在對數據的合法性檢驗上,當更新的數據不滿足表或系統的一致性要求,則通過拋出異常的方式拒絕事務,在其上層的代碼可以捕獲這個異常并進行相應操作。  
    79.   
    80. 下面將通過舉例說明,在例子中將觸發器主體的語法一一介紹,讀者可以在例子中體會觸發器的功能。  
    81.   
    82. 4,例一:行級觸發器之一  
    83. CREATE OR REPLACE TRIGGER salary_raiu  
    84. AFTER INSERT OR UPDATE OF amount ON salary  
    85. FOR EACH ROW  
    86. BEGIN  
    87.     IF inserting THEN  
    88.         dbms_output.put_line(‘插入’);  
    89.     ELSIF updating THEN  
    90. dbms_output.put_line(‘更新amount列’);  
    91.     END IF;  
    92. END;  
    93. 以上是一個after insert和after update的行級觸發器。在第二行中of amount on salary的意思是只有當amount列被更新時,update觸發器才會有效。所以,以下語句將不會執行觸發器:  
    94. Update salary set month = ‘200601’ where month = ‘200606’;  
    95. 在觸發器主體的if語句表達式中,inserting, updating和deleting可以用來區分當前是在做哪一種DML操作,可以作為把多個類似觸發器合并在一個觸發器中判別觸發事件的屬性。  
    96.   
    97. 5,例二:行級觸發器之二  
    98. 新建員工表employment  
    99. CREATE TABLE EMPLOYMENT  
    100. (  
    101.   EMPLOYEE_ID NUMBER, --員工ID  
    102.   MAXSALARY   NUMBER --工資上限  
    103. )  
    104. 插入兩條記錄  
    105. Insert into employment values(11000);  
    106. Insert into employment values(22000);  
    107.   
    108. CREATE OR REPLACE TRIGGER salary_raiu  
    109. AFTER INSERT OR UPDATE OF amount ON salary  
    110. FOR EACH ROW  
    111. WHEN ( NEW.amount >= 1000 AND (old.amount IS NULL OR OLD.amount <= 500))  
    112. DECLARE  
    113.     v_maxsalary NUMBER;  
    114. BEGIN  
    115.     SELECT maxsalary  
    116.         INTO v_maxsalary  
    117.         FROM employment  
    118.      WHERE employee_id = :NEW.employee_id;  
    119.     IF :NEW.amount > v_maxsalary THEN  
    120.         raise_application_error(-20000'工資超限');  
    121.     END IF;  
    122. END;  
    123.   
    124. 以上的例子引入了一個新的表employment,表中的maxsalary字段代表該員工每月所能分配的最高工資。下面的觸發器根據插入或修改記錄的 employee_id,在employment表中查到該員工的每月最高工資,如果插入或修改后的amount超過這個值,則報錯誤。  
    125. 代碼中的when子句表明了該觸發器只針對修改或插入后的amount值超過1000,而修改前的amount值小于500的記錄。New對象和old對象分別表示了操作前和操作后的記錄對象。對于insert操作,由于當前操作記錄無歷史對象,所以old對象中所有屬性是null;對于delete操作,由于當前操作記錄沒有更新對象,所以new對象中所有屬性也是null。但在這兩種情況下,并不影響old和new對象的引用和在觸發器主體中的使用,和普通的空值作同樣的處理。  
    126. 在觸發器主體中,先通過:new.employee_id,得到該員工的工資上限,然后在if語句中判斷更新后的員工工資是否超限,如果超限則錯誤代碼為-20000,錯誤信息為“工資超限”的自定義錯誤。其中的raise_application_error包含兩個參數,前一個是自定義錯誤代碼,后一個是自定義錯誤代碼信息。其中自定義錯誤代碼必須小于或等于-20000。執行完該語句后,一個異常被拋出,如果在上一層有exception子句,該異常將被捕獲。如下面代碼:  
    127. DECLARE  
    128.     code NUMBER;  
    129.     msg  VARCHAR2(500);  
    130. BEGIN  
    131.     INSERT INTO salary (employee_id, amount) VALUES (25000);  
    132. EXCEPTION  
    133.     WHEN OTHERS THEN  
    134.         code := SQLCODE;  
    135.         msg  := substr(SQLERRM, 1500);  
    136.         dbms_output.put_line(code);  
    137.         dbms_output.put_line(msg);  
    138. END;  
    139. 執行后,將在output中或者sqlplus窗口中見著以下信息:  
    140. -20000  
    141. ORA-20000: 工資超出限制  
    142. ORA-06512: 在"SCOTT.SALARY_RAI", line 9  
    143. ORA-04088: 觸發器 'SCOTT.SALARY_RAI' 執行過程中出錯  
    144.   
    145. 這里的raise_application_error相當于拒絕了插入或者修改事務,當上層代碼接受到這個異常后,判斷該異常代碼等于-20000,可以作出回滾事務或者繼續其他事務的處理。  
    146.   
    147. 以上兩個例子中用到的inserting, updating, deleting和raise_application_error都是dbms_standard包中的函數,具體的說明可以參照Oracle的幫助文檔。  
    148. create or replace package sys.dbms_standard is  
    149.   procedure raise_application_error(num binary_integer, msg varchar2,  
    150.   function inserting return boolean;  
    151.   function deleting  return boolean;  
    152.   function updating  return boolean;  
    153.   function updating (colnam varchar2) return boolean;  
    154. end;  
    155.   
    156. 對于before和after行級觸發器,:new和:old對象的屬性值都是一樣的,主要是對于在Oracle約束(Constraint)之前或之后的執行觸發器的選擇。需要注意的是,可以在before行觸發器中更改:new對象中的值,但是在after行觸發器就不行。  
    157.   
    158. 下面介紹一種instead of觸發器,該觸發器主要使用在對視圖的更新上,以下是instead of觸發器的語法:  
    159. CREATE OR REPLACE TRIGGER trigger_name  
    160. INSTEAD OF <insert | update | delete> ON view_name  
    161. [FOR EACH ROW]  
    162. WHEN (condition)  
    163. DECLARE  
    164. BEGIN  
    165.     --觸發器代碼  
    166. END;  
    167.   
    168. 其他部分語法同前面所述的before和after語法是一樣的,唯一不同的是在第二行用上了instead of關鍵字。對于普通的視圖來說,進行 insert等操作是被禁止的,因為Oracle無法知道操作的字段具體是哪個表中的字段。但我們可以通過建立instead of觸發器,在觸發器主體中告訴Oracle應該更新,刪除或者修改哪些表的哪部分字段。如:  
    169.   
    170. 6,例三:instead of觸發器  
    171. 新建視圖  
    172. CREATE VIEW employee_salary(employee_id, maxsalary, MONTH, amount) AS   
    173. SELECT a.employee_id, a.maxsalary, b.MONTH, b.amount  
    174. FROM employment a, salary b  
    175. WHERE a.employee_id = b.employee_id  
    176.   
    177. 如果執行插入語句  
    178. INSERT INTO employee_salary(employee_id, maxsalary, MONTH, amount)  
    179. VALUES(10100000'200606'10000);  
    180. 系統會報錯:  
    181. ORA-01779:無法修改與非鍵值保存表對應的列  
    182.   
    183. 我們可以通過建立以下的instead of存儲過程,將插入視圖的值分別插入到兩個表中:  
    184. create or replace trigger employee_salary_rii  
    185.   instead of insert on employee_salary    
    186.   for each ROW  
    187. DECLARE  
    188.     v_cnt NUMBER;  
    189. BEGIN  
    190.   --檢查是否存在該員工信息  
    191.     SELECT COUNT(*)  
    192.         INTO v_cnt  
    193.         FROM employment  
    194.      WHERE employee_id = :NEW.employee_id;  
    195.     IF v_cnt = 0 THEN  
    196.         INSERT INTO employment  
    197.             (employee_id, maxsalary)  
    198.         VALUES  
    199.             (:NEW.employee_id, :NEW.maxsalary);  
    200.     END IF;  
    201.   --檢查是否存在該員工的工資信息  
    202.     SELECT COUNT(*)  
    203.         INTO v_cnt  
    204.         FROM salary  
    205.      WHERE employee_id = :NEW.employee_id  
    206.          AND MONTH = :NEW.MONTH;  
    207.     IF v_cnt = 0 THEN  
    208.         INSERT INTO salary  
    209.             (employee_id, MONTH, amount)  
    210.         VALUES  
    211.             (:NEW.employee_id, :NEW.MONTH, :NEW.amount);  
    212.     END IF;  
    213. END employee_salary_rii;  
    214.   
    215. 該觸發器被建立后,執行上述insert操作,系統就會提示成功插入一條記錄。  
    216. 但需要注意的是,這里的“成功插入一條記錄”,只是Oracle并未發現觸發器中有異常拋出,而根據insert語句中涉及的記錄數作出一個判斷。若觸發器的主體什么都沒有,只是一個空語句,Oracle也會報“成功插入一條記錄”。同樣道理,即使在觸發器主體里往多個表中插入十條記錄,Oracle的返回也是“成功插入一條記錄”。  
    217.   
    218.   
    219.   
    220.   
    221. 行級觸發器可以解決大部分的問題,但是如果需要對本表進行掃描檢查,比如要檢查總的工資是否超限了,用行級觸發器是不行的,因為行級觸發器主體中不能有涉及到關聯表的事務,這時就需要用到語句級觸發器。以下是語句級觸發器的語法:  
    222. CREATE OR REPLACE TRIGGER trigger_name  
    223. <before | after | instead of ><insert | update | delete > ON table_name  
    224. DECLARE  
    225. BEGIN  
    226.     --觸發器主體  
    227. END;  
    228.   
    229. 從語法定義上來看,行級觸發器少了for each row,也不能使用when子句來限定入口條件,其他部分都是一樣的,包括insert, update, delete和instead of都可以使用。  
    230.   
    231.   
    232. 7,例四:語句級觸發器之一  
    233. CREATE OR REPLACE TRIGGER salary_saiu  
    234. AFTER INSERT OR UPDATE OF amount ON salary  
    235. DECLARE  
    236.     v_sumsalary NUMBER;  
    237. BEGIN  
    238.   SELECT SUM(amount) INTO v_sumsalary FROM salary;  
    239.     IF v_sumsalary > 500000 THEN  
    240.         raise_application_error(-20001'總工資超過500000');  
    241.     END IF;  
    242. END;  
    243.   
    244. 以上代碼定義了一個語句級觸發器,該觸發器檢查在insert和update了amount字段后操作后,工資表中所有工資記錄累加起來是否超過500000,如果超過則拋出異常。從這個例子可以看出,語句級觸發器可以對關聯表表進行掃描,掃描得到的結果可以用來作為判斷一致性的標志。需要注意的是,在 before語句觸發器主體和after語句觸發器主體中對關聯表進行掃描,結果是不一樣的。在before語句觸發器主體中掃描,掃描結果將不包括新插入和更新的記錄,也就是說當以上代碼換成 before觸發器后,以下語句將不報錯:  
    245. INSERT INTO salary(employee_id, month, amount) VALUEs(2'200601'600000)  
    246. 這是因為在主體中得到的v_sumsalary并不包括新插入的600000工資。  
    247. 另外,在語句級觸發器中不能使用:new和:old對象,這一點和行級觸發器是顯著不同的。如果需要檢查插入或更新后的記錄,可以采用臨時表技術。  
    248. 臨時表是一種Oracle數據庫對象,其特點是當創建數據的進程結束后,進程所創建的數據也隨之清除。進程與進程不可以互相訪問同一臨時表中對方的數據,而且對臨時表進行操作也不產生undo日志,減少了數據庫的消耗。具體有關臨時表的知識,可以參看有關書籍。  
    249. 為了在語句級觸發器中訪問新插入后修改后的記錄,可以增加行級觸發器,將更新的記錄插入臨時表中,然后在語句級觸發器中掃描臨時表,獲得修改后的記錄。臨時表的表結構一般與關聯表的結構一致。  
    250.   
    251.   
    252. 8,例五:語句級觸發器之二  
    253. 目的:限制每個員工的總工資不能超過50000,否則停止對該表操作。  
    254. 創建臨時表  
    255. create global temporary table SALARY_TMP  
    256. (  
    257.   EMPLOYEE_ID NUMBER,  
    258.   MONTH       VARCHAR2(6),  
    259.   AMOUNT      NUMBER  
    260. )  
    261. on commit delete rows;  
    262.   
    263. 為了把操作記錄插入到臨時表中,創建行級觸發器:  
    264. CREATE OR REPLACE TRIGGER salary_raiu  
    265. AFTER INSERT OR UPDATE OF amount ON salary  
    266. FOR EACH ROW  
    267. BEGIN  
    268.   INSERT INTO salary_tmp(employee_id, month, amount)  
    269.   VALUES(:NEW.employee_id, :NEW.MONTH, :NEW.amount);  
    270. END;  
    271. 該觸發器的作用是把更新后的記錄信息插入到臨時表中,如果更新了多條記錄,則每條記錄都會保存在臨時表中。  
    272.   
    273. 創建語句級觸發器:  
    274. CREATE OR REPLACE TRIGGER salary_sai  
    275. AFTER INSERT OR UPDATE OF amount ON salary  
    276. DECLARE  
    277.     v_sumsalary NUMBER;  
    278. BEGIN  
    279.     FOR cur IN (SELECT * FROM salary_tmp) LOOP  
    280.         SELECT SUM(amount)  
    281.             INTO v_sumsalary  
    282.             FROM salary  
    283.          WHERE employee_id = cur.employee_id;  
    284.         IF v_sumsalary > 50000 THEN  
    285.             raise_application_error(-20002'員工累計工資超過50000');  
    286.         END IF;  
    287.     DELETE FROM salary_tmp;  
    288.     END LOOP;  
    289. END;  
    290.   
    291. 該觸發器首先用游標從salary_tmp臨時表中逐條讀取更新或插入的記錄,取employee_id,在關聯表salary中查找所有相同員工的工資記錄,并求和。若某員工工資總和超過50000,則拋出異常。如果檢查通過,則清空臨時表,避免下次檢查相同的記錄。  
    292. 執行以下語句:  
    293. INSERT INTO salary(employee_id, month, amount) VALUEs(7'200601'20000);  
    294. INSERT INTO salary(employee_id, month, amount) VALUEs(7'200602'20000);  
    295. INSERT INTO salary(employee_id, month, amount) VALUEs(7'200603'20000);  
    296. 在執行第三句時系統報錯:  
    297. ORA-20002:員工累計工資超過50000  
    298. 查詢salary表,發現前兩條記錄正常插入了,第三條記錄沒有插入。  
    299.   
    300.   
    301. 如果系統結構比較復雜,而且觸發器的代碼比較多,在觸發器主體中寫過多的代碼,對于維護來說是一個困難。這時可以將所有觸發器的代碼寫到同一個包中,不同的觸發器代碼以不同的存儲過程封裝,然后觸發器主體中調用這部分代碼。  
    302.   
    303. 9,例六:用包封裝觸發器代碼  
    304. 目的:改寫例五,封裝觸發器主體代碼  
    305. 創建代碼包:  
    306. CREATE OR REPLACE PACKAGE BODY salary_trigger_pck IS  
    307.   
    308.     PROCEDURE load_salary_tmp(i_employee_id IN NUMBER,  
    309.                             i_month       IN VARCHAR2,  
    310.                             i_amount      IN NUMBER) IS  
    311.     BEGIN  
    312.         INSERT INTO salary_tmp VALUES (i_employee_id, i_month, i_amount);  
    313.     END load_salary_tmp;  
    314.   
    315.     PROCEDURE check_salary IS  
    316.         v_sumsalary NUMBER;  
    317.     BEGIN  
    318.         FOR cur IN (SELECT * FROM salary_tmp) LOOP  
    319.             SELECT SUM(amount)  
    320.                 INTO v_sumsalary  
    321.                 FROM salary  
    322.              WHERE employee_id = cur.employee_id;  
    323.             IF v_sumsalary > 50000 THEN  
    324.                 raise_application_error(-20002'員工累計工資超過50000');  
    325.             END IF;  
    326.             DELETE FROM salary_tmp;  
    327.         END LOOP;  
    328.     END check_salary;  
    329. END salary_trigger_pck;  
    330. 包salary_trigger_pck中有兩個存儲過程,load_salary_tmp用于在行級觸發器中調用,往salary_tmp臨時表中裝載更新或插入記錄。而check_salary用于在語句級觸發器中檢查員工累計工資是否超限。  
    331.   
    332. 修改行級觸發器和語句級觸發器:  
    333. CREATE OR REPLACE TRIGGER salary_raiu  
    334.     AFTER INSERT OR UPDATE OF amount ON salary  
    335.     FOR EACH ROW  
    336. BEGIN  
    337.     salary_trigger_pck.load_salary_tmp(:NEW.employee_id,     :NEW.MONTH, :NEW.amount);  
    338. END;  
    339.   
    340. CREATE OR REPLACE TRIGGER salary_sai  
    341. AFTER INSERT OR UPDATE OF amount ON salary  
    342. BEGIN  
    343.     salary_trigger_pck.check_salary;  
    344. END;  
    345.   
    346. 這樣主要代碼就集中到了salary_trigger_pck中,觸發器主體中只實現了一個調用功能。  
    347.   
    348. 10,觸發器命名規范  
    349. 為了方便對觸發器命名和根據觸發器名稱了解觸發器含義,需要定義觸發器的命名規范:  
    350. Trigger_name = table_name_trg_<R|S><A|B|I><I|U|D>  
    351.   
    352. 觸發器名限于30個字符。必須縮寫表名,以便附加觸發器屬性信息。  
    353. <R|S>基于行級(row)還是語句級(statement)的觸發器  
    354. <A|B|I>after, before或者是instead of觸發器  
    355. <I|U|D>觸發事件是insert,update還是delete。如果有多個觸發事件則連著寫  
    356.   
    357. 例如:  
    358. Salary_rai      salary表的行級after觸發器,觸發事件是insert  
    359. Employee_sbiud  employee表的語句級before觸發器,觸發事件是insert,update和delete  
    posted @ 2010-08-04 15:27 redcoatjk 閱讀(323) | 評論 (0)編輯 收藏
     

    經常在用apache和tomcat等這些服務器,可是總感覺還是不清楚他們之間有什么關系,在用tomcat的時候總出現apache,總感到迷惑,到底誰是主誰是次,因此特意在網上查詢了一些這方面的資料,總結了一下:

     

    apache支持靜態頁,tomcat支持動態的,比如servlet等,


    一般使用apache+tomcat的話,apache只是作為一個轉發,對jsp的處理是由tomcat來處理的。

    apache可以支持php\cgi\perl,但是要使用java的話,你需要tomcat在apache后臺支撐,將java請求由apache轉發給tomcat處理。

    apache是web服務器,Tomcat是應用(java)服務器,它只是一個servlet(jsp也翻譯成servlet)容器,可以認為是apache的擴展,但是可以獨立于apache運行。

     

    這兩個有以下幾點可以比較的:
    1、兩者都是apache組織開發的
    2、兩者都有HTTP服務的功能
    3、兩者都是免費的

    不同點:
    Apache是專門用了提供HTTP服務的,以及相關配置的(例如虛擬主機、URL轉發等等)
    Tomcat是Apache組織在符合J2EE的JSP、Servlet標準下開發的一個JSP服務器

     

     

    二:

    APACHE是一個web服務器環境程序 啟用他可以作為web服務器使用 不過只支持靜態網頁 如(asp,php,cgi,jsp)等動態網頁的就不行
    如果要在APACHE環境下運行jsp 的話就需要一個解釋器來執行jsp網頁 而這個jsp解釋器就是TOMCAT, 為什么還要JDK呢?因為jsp需要連接數據庫的話 就要jdk來提供連接數據庫的驅程,所以要運行jsp的web服務器平臺就需要APACHE+TOMCAT+JDK

    整合的好處是:
    如果客戶端請求的是靜態頁面,則只需要Apache服務器響應請求
    如果客戶端請求動態頁面,則是Tomcat服務器響應請求
    因為jsp是服務器端解釋代碼的,這樣整合就可以減少Tomcat的服務開銷

     

     

    三:

    apache:側重于http server
    tomcat:側重于servlet引擎,如果以standalone方式運行,功能上與apache等效 , 支持JSP,但對靜態網頁不太理想;
    apache是web服務器,tomcat是應用(java)服務器,它只是一個servlet(jsp也翻譯成servlet)容器,可以認為是apache的擴展,但是可以獨立于apache運行。
    換句話說,apache是一輛卡車,上面可以裝一些東西如html等。但是不能裝水,要裝水必須要有容器(桶),而這個桶也可以不放在卡車上。

    posted @ 2010-08-02 16:03 redcoatjk 閱讀(194) | 評論 (0)編輯 收藏
     
         摘要:       proxool一個數據庫連接池框架,提供了對你選擇的其它類型的驅動程序的連接池封裝。可以非常簡單的移植到現存的代碼中。完全可配置。快速,成熟,健 壯。可以透明地為你現存的JDBC驅動程序增加連接池功能。到目前為止最新版本是proxool 0.9.1,可從官網下載最新版本http://proxool.sourceforge.net ...  閱讀全文
    posted @ 2010-07-29 12:17 redcoatjk 閱讀(619) | 評論 (0)編輯 收藏
     
    載自http://xiaoxinshome.javaeye.com/blog/139609
    --------
    語法規則:
    Create [or replace] trigger [模式.]觸發器名

           Before| after   insert|delete|(update of 列名)

    On 表名

    [for each row]

    When 條件

    PL/SQL塊

    說明:

    For each row的意義是:在一次操作表的語句中,每操作成功一行就會觸發一 次;不寫的話,表示是表級觸發器,則無論操作多少行,都只觸發一次;

    When條件的出現說明了,在DML操作的時候也許一定會觸發觸發器,但是觸發器不一定會做實際的工作,比如when 后的條件不為真的時候,觸發器只是簡單地跳過了PL/SQL塊;

    例子:

    sql 代碼
    1. create or replace trigger wf_tri_user_list before insert or update or delete on user_list   
    2. for each row   
    3. declare  
    4.    uid varchar2(10); useq varchar2(10); asql varchar2(200); namea varchar2(200); nameb varchar2(200);   
    5. begin  
    6.    namea:=NULL;   
    7.    nameb:=NULL;   
    8.    if inserting then  
    9.       insert into wflow.bpm_org_user(userid,username,diaplayname,seq) values(:NEW.user_id,:NEW.user_name,:NEW.user_realname,:NEW.user_id);   
    10.       dbms_output.put_line('insert trigger is chufale .....');   
    11.         
    12.    end if;   
    13.    if updating then  
    14.       if (:NEW.user_name<>:OLD.user_name) and (:NEW.user_realname<>:OLD.user_realname) then  
    15.          namea:=:NEW.user_name;   
    16.          nameb:=:NEW.user_realname;   
    17.          asql:='update wflow.bpm_org_user set diaplayname=:1 where username=:2';   
    18.          execute immediate asql using namea,nameb;   
    19.       else  
    20.         if :NEW.user_name<>:OLD.user_name then  
    21.           namea:=:NEW.user_name;   
    22.           asql:='update wflow.bpm_org_user set user_name=:1 where username=:2';   
    23.           execute immediate asql using namea;   
    24.         else  
    25.           if :NEW.user_realname<>:OLD.user_realname then  
    26.             nameb:=:NEW.user_realname;   
    27.             asql:='update wflow.bpm_org_user set diaplayname=:1 where username=:2';   
    28.             execute immediate asql using nameb,:OLD.user_id;   
    29.           end if;   
    30.         end if;   
    31.       end if;   
    32.    end if;   
    33.    if deleting then  
    34.       update wflow.bpm_org_jobusers set userid = 0 where :OLD.user_id =userid and parentid=-1;   
    35.       delete from wflow.bpm_org_jobusers where userid = :OLD.user_id;   
    36.       delete wflow.bpm_org_user where userid=:OLD.user_id;   
    37.    end if;   
    38.    commit;   
    39. end;   
    40.   

     

    關鍵字:

    :NEW 和:OLD使用方法和意義,new 只出現在insert和update時,old只出現在update和delete時。在insert時new表示新插入的行數據,update時new 表示要替換的新數據、old表示要被更改的原來的數據行,delete時old表示要被刪除的數據。

    注意:

    在觸發器中不能使用commit。



    posted @ 2010-06-12 11:49 redcoatjk 閱讀(148) | 評論 (0)編輯 收藏
     
     DECODE()函數,它將輸入數值與函數中的參數列表相比較,根據輸入值返回一個對應值。函數的參數列表是由若干數值及其對應結果值組成的若干序偶形 式。當然,如果未能與任何一個實參序偶匹配成功,則函數也有默認的返回值。

    區別于SQL的其它函數,DECODE函數還能識別和操作空值。

    語法:DECODE(control_value,value1,result1[,value2,result2…] [,default_result]);

    control _value試圖處理的數值。DECODE函數將該數值與后面的一系列的偶序相比較,以決定返回值。

    value1是一組成序偶的數值。如果輸入數值與之匹配成功,則相應的結果將被返回。對應一個空的返回值,可以使用關鍵字NULL于之對應

    result1 是一組成序偶的結果值。

    default_result 未能與任何一個值匹配時,函數返回的默認值。

    例如:

    selectdecode( x , 1 , ‘x is 1 ’, 2 , ‘x is 2 ’, ‘others’) from dual

    當x等于1時,則返回‘x is 1’。

    當x等于2時,則返回‘x is 2’。

    否則,返回others’。

    需要,比較2個值的時候,可以配合SIGN()函數一起使用。

    SELECT DECODE( SIGN(5 -6), 1 'Is Positive', -1, 'Is Nagative', 'Is Zero')

    同樣,也可以用CASE實現:

    SELECT CASE SIGN(5 - 6)

    WHEN 1 THEN 'Is Positive'

    WHEN -1 THEN 'Is Nagative'

    ELSE 'Is Zero' END

    FROM DUAL

    此外,還可以在Order by中使用Decode。

    例如:表table_subject,有subject_name列。要求按照:語、數、外的順序進行排序。這時,就可以非常輕松的使用 Decode完成要求了。

    select * from table_subject order by decode(subject_name, '語文', 1, '數學', 2, , '外語',3)
    posted @ 2010-06-12 11:05 redcoatjk 閱讀(175) | 評論 (0)編輯 收藏
     

    Unique約束

    Unique約束可應用于一列或多列字段上。如果字段值存在,必須為唯一的,可以取null值
    1、一張表只能有一個PK約束但可以有多個Unique約束
    2、作為PK的字段不能為null,但作為Unique的字段可以為null,但不為null的行必須是Unique的
    3、當創建一個PK時,創建一個Index,創建一個Unique時也創建一個Index
    4、PK和Unique約束字段可以作為FK的父親。FK約束字段引用PK約束,也引用Unique約束

    創建語句:
      CREATE TABLE temp (pk NUMBER PRIMARY KEY, a NUMBER, b NUMBER);
         ALTER TABLE temp ADD CONSTRAINT uk_temp_a_b UNIQUE (a, b);

    Example:
    CREATE TABLE students
     (student_id    VARCHAR2(10) NOT NULL,
      student_name  VARCHAR2(30) NOT NULL,
      college_major VARCHAR2(15) NOT NULL,
      status        VARCHAR2(15) NOT NULL,
      state         VARCHAR2(2),
      license_no    VARCHAR2(30)) TABLESPACE student_data;

    ALTER TABLE students
      ADD CONSTRAINT pk_students PRIMARY KEY (student_id)
      USING INDEX TABLESPACE student_index;

    ALTER TABLE students
      ADD CONSTRAINT uk_students_license
      UNIQUE (state, license_no)
      USING INDEX TABLESPACE student_index;

    ALTER TABLE students
       ADD CONSTRAINT ck_students_st_lic
       CHECK ((state IS NULL AND license_no IS NULL) OR
              (state IS NOT NULL AND license_no is NOT NULL));
    posted @ 2010-05-27 09:49 redcoatjk 閱讀(361) | 評論 (0)編輯 收藏
     
    作為開源的連接池Proxool
    有以下優點。
    透明性   可以明的添加接連池而不影響你原來的項目的JDBC代碼;
    開放性 你可以方便的與其它的開源產品進行整合。如hibernate  中自帶的這個Proxool
    標準性 它是在J2SE下開出來的。你可以放心的開發
    易用性  非常容易 的進行配置。
    proxool是一個非常強大的連接池工具包,我覺得相比dbcp、c3p0這兩個連接池包都要好用,我用loadrunner測試過,這三個連 接池的從性能上排名如下:proxool>c3p0>dbcp,特別是dbcp在大并發的情況下總是出現各種異常。

    下面是實現proxool的幾種方式:

    JDBC連接方法:
    首先建一個proxool的配置文件proxool.xml

    proxool.xml 代碼
    xml 代碼

    <!--sp-->xml version="1.0" encoding="UTF-8"?>    
      
    <!-- the proxool configuration can be embedded within your own application's. Anything outside the "proxool" tag is ignored. -->    
      
    <something-else-entirely>  
        <proxool>  
            <!--連接池的別名-->  
            <alias>DBPool</alias>  
            <!--proxool只能管理由自己產生的連接-->  
            <driver-url>jdbc:oracle:thin:@192.168.0.40:1521:drcom</driver-url>  
            <!--JDBC驅動程序-->  
            <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>  
            <driver-properties>  
                <property name="user" value="drcom"/>  
                <property name="password" value="drcom"/>  
            </driver-properties>  
            <!-- proxool自動偵察各個連接狀態的時間間隔(毫秒),偵察到空閑的連接就馬上回收,超時的銷毀-->  
            <house-keeping-sleep-time>90000</house-keeping-sleep-time>  
            <!-- 指因未有空閑連接可以分配而在隊列中等候的最大請求數,超過這個請求數的用戶連接就不會被接受-->    
            <maximum-new-connections>150</maximum-new-connections>  
            <!-- 最少保持的空閑連接數-->    
            <prototype-count>3</prototype-count>  
            <!-- 允許最大連接數,超過了這個連接,再有請求時,就排在隊列中等候,最大的等待請求數由maximum-new-connections決定-->    
            <maximum-connection-count>100</maximum-connection-count>  
            <!-- 最小連接數-->  
            <minimum-connection-count>3</minimum-connection-count>  
        </proxool>  
    </something-else-entirely>  

    再在web.xml中進行配置,其中的ServletConfigurator是裝載WEB-INF目錄下的proxool.xml,并設置為Tomcat啟動時就加載。Admin這個Servlet是proxool提供的察看連接池的信息的工具,


    web.xml 代碼
    xml 代碼

    <servlet>
          <servlet-name>proxoolServletConfigurator</servlet-name>
         <servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
         <init-param>
           <param-name>xmlFile</param-name>
           <param-value>WEB-INF/config/proxool.xml</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
       </servlet>
    <!-- proxool提供的管理監控工具,可查看當前數據庫連接情況。如果運行不成功,請刪除本行 -->
    <servlet>
        <servlet-name>Admin</servlet-name>
          <servlet-class>org.logicalcobwebs.proxool.admin.servlet.AdminServlet</servlet-class>
       </servlet>
       <servlet-mapping>
        <servlet-name>Admin</servlet-name>
        <url-pattern>/admin</url-pattern>
       </servlet-mapping>  


    以上配置完成后,第三步就可以創建一個連接池的類了


    java 代碼

    package selfservice;        
          
    import java.sql.Connection;        
    import java.sql.DriverManager;        
    import java.sql.ResultSet;        
    import java.sql.SQLException;        
    import java.sql.Statement;        
          
    import org.logicalcobwebs.proxool.ProxoolException;        
    import org.logicalcobwebs.proxool.ProxoolFacade;        
    import org.logicalcobwebs.proxool.admin.SnapshotIF;        
          
          
    public class PoolManager {        
                
        private static int activeCount = 0;        
                
                
        public PoolManager(){        
                    
        }          
        /**      
         * 獲取連接      
         * getConnection      
         * @param name      
         * @return      
         */      
        public Connection getConnection() {        
            try{        
                Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");//proxool驅動類        
                Connection conn = DriverManager.getConnection("proxool.DBPool");    
               //此處的DBPool是在proxool.xml中配置的連接池別名      
                showSnapshotInfo();        
                        
                return conn;        
            }catch(Exception ex){        
                ex.printStackTrace();        
            }        
            return null;        
        }        
        /**      
         * 此方法可以得到連接池的信息      
         * showSnapshotInfo      
         */      
        private void showSnapshotInfo(){        
            try{        
                SnapshotIF snapshot = ProxoolFacade.getSnapshot("DBPool", true);        
                int curActiveCount=snapshot.getActiveConnectionCount();//獲得活動連接數        
                int availableCount=snapshot.getAvailableConnectionCount();//獲得可得到的連接數        
                int maxCount=snapshot.getMaximumConnectionCount() ;//獲得總連接數        
                if(curActiveCount!=activeCount)//當活動連接數變化時輸出的信息        
                {        
                 System.out.println("活動連接數:"+curActiveCount+"(active)  可得到的連接數:"+availableCount+"(available)  總連接數:"+maxCount+"(max)");                    
                 activeCount=curActiveCount;        
                }        
            }catch(ProxoolException e){        
                e.printStackTrace();        
            }        
        }        
        /**      
         * 獲取連接      
         * getConnection      
         * @param name      
         * @return      
         */      
        public Connection getConnection(String name){        
            return getConnection();        
        }        
        /**      
         * 釋放連接      
         * freeConnection      
         * @param conn      
         */      
        public void freeConnection(Connection conn){        
            if(conn!=null){        
                try {        
                    conn.close();        
                } catch (SQLException e) {                      
                    e.printStackTrace();        
                }        
            }        
        }        
        /**      
         * 釋放連接      
         * freeConnection      
         * @param name      
         * @param con      
         */      
        public void freeConnection (String name,Connection con){        
            freeConnection(con);        
        }        
                
        public void getQuery() {                
            try {        
                Connection conn = getConnection();        
                if(conn != null){        
                    Statement statement = conn.createStatement();        
                    ResultSet rs = statement.executeQuery("select * from tblgxinterface");        
                    int c = rs.getMetaData().getColumnCount();        
                    while(rs.next()){                          
                        System.out.println();        
                        for(int i=1;i<=c;i++){        
                            System.out.print(rs.getObject(i));        
                        }        
                    }        
                    rs.close();        
                }        
                freeConnection(conn);        
            } catch (SQLException e) {                  
                e.printStackTrace();        
            }        
          
        }        
          
    }      


    就這樣我們完成了一個連接池的功能。proxool的連接池我用loadrunner進行大并發的測試,性能還是很好的。

    Hibernate中proxool連接池的方式:

    首先步驟跟JDBC的連接池一樣,也是新建一個proxool.xml配置文件,再在web.xml中配置,具體參考上面。
    第二步在hibernate的配置文件hibernate.cfg.xml中配置proxool連接設置:


    hibernate.cfg.xml代碼
    xml 代碼

    <?xmlversion='1.0'encoding='UTF-8'?>

    <!DOCTYPEhibernate-configurationPUBLIC

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

    <session-factory>

    <property name="hibernate.connection.provider_class">org.hibernate.connection.ProxoolConnectionProvider</property>

    <property name="hibernate.proxool.pool_alias">DBPool</property>

    <property name="hibernate.proxool.xml">Proxool.xml</property>

    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

    <mappin gresource="hibernate.cfg.xml"/>

    這里放Hibernate的映射文件

    </session-factory>

      

    Spring中proxool連接池的方式:

    首先布驟與JDBC的連接池一樣,先建一個proxool.xml配置文件,再在web.xml中配置,具體參考上面的。
    第二步在spring配置文件applicationContext.xml中配置proxool連接設置

    applicationContext.xml代碼
    xml 代碼

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" singleton="true">  
            <property name="driverClassName" value="org.logicalcobwebs.proxool.ProxoolDriver"/>  
            <property name="url" value="proxool.StatDBPool"/>  
        </bean>  
        <bean id="transactionManager"    
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
            <property name="dataSource">  
                <ref local="dataSource" />  
            </property>  
        </bean>    


    這樣spring就能得到一個dataSource的數據源。


    proxool還有很多功能,我這只是簡單的應用。具體請察看proxool用戶指南。
    posted @ 2010-05-25 10:21 redcoatjk 閱讀(281) | 評論 (0)編輯 收藏
     
    概述
    J2SE(TM) 5.0引入了很多激進的語言元素變化,這些變化或多或少減輕了我們開發人員的一些編碼負擔,其中的大部分也必然會被應用到即將發布的J2EE(TM) 5.0中。主要的新特性包括:

      · 泛型

      · 增強的for循環

      · 自動裝箱和自動拆箱

      · 類型安全的枚舉

      · 可變長度參數

      · 靜態引入

      · 元數據(注解)

      · C風格的格式化輸出

      這當中,泛型、枚舉和注解可能會占用較大的篇幅,而其余的因為用法直截了當,抑或相對簡單,我就稍作介紹,剩下的留給讀者去思考、去探索了。

      1.4. 泛型

      泛型這個題目相當大,大到完全可以就這個話題寫一本書。有關Java是否需要泛型和如何實現泛型的討論也早就在Java社群廣為流傳。終于,我們在J2SE(TM) 5.0中看到了它。也許目前Java對泛型的支持還算不上足夠理想,但這一特性的添加也經足以讓我們欣喜一陣了。

      在接下來的介紹中,我們會了解到:Java的泛型雖然跟C++的泛型看上去十分相似,但其實有著相當大的區別,有些細節的東西也相當復雜(至少很多地方會跟我們的直覺背道而馳)。可以這樣說,泛型的引入在很大程度上增加了Java語言的復雜度,對初學者尤其是個挑戰。下面我們將一點一點往里挖。

      首先我們來看一個簡單的使用泛型類的例子:

        ArrayList<Integer> aList = new ArrayList<Integer>();
        aList.add(new Integer(1));
        // ...
        Integer myInteger = aList.get(0);

      我們可以看到,在這個簡單的例子中,我們在定義aList的時候指明了它是一個直接受Integer類型的ArrayList,當我們調用aList.get(0)時,我們已經不再需要先顯式的將結果轉換成Integer,然后再賦值給myInteger了。而這一步在早先的Java版本中是必須的。也許你在想,在使用Collection時節約一些類型轉換就是Java泛型的全部嗎?遠不止。單就這個例子而言,泛型至少還有一個更大的好處,那就是使用了泛型的容器類變得更加健壯:早先,Collection接口的get()和Iterator接口的next()方法都只能返回Object類型的結果,我們可以把這個結果強制轉換成任何Object的子類,而不會有任何編譯期的錯誤,但這顯然很可能帶來嚴重的運行期錯誤,因為在代碼中確定從某個Collection中取出的是什么類型的對象完全是調用者自己說了算,而調用者也許并不清楚放進Collection的對象具體是什么類的;就算知道放進去的對象“應該”是什么類,也不能保證放到Collection的對象就一定是那個類的實例。現在有了泛型,只要我們定義的時候指明該Collection接受哪種類型的對象,編譯器可以幫我們避免類似的問題溜到產品中。我們在實際工作中其實已經看到了太多的ClassCastException,不是嗎?

      泛型的使用從這個例子看也是相當易懂。我們在定義ArrayList時,通過類名后面的<>括號中的值指定這個ArrayList接受的對象類型。在編譯的時候,這個ArrayList會被處理成只接受該類或其子類的對象,于是任何試圖將其他類型的對象添加進來的語句都會被編譯器拒絕。

      那么泛型是怎樣定義的呢?看看下面這一段示例代碼:(其中用E代替在實際中將會使用的類名,當然你也可以使用別的名稱,習慣上在這里使用大寫的E,表示Collection的元素。)

        public class TestGenerics<E> {
        Collection<E> col;
        public void doSth(E elem) {
        col.add(elem);
        // ...
        }
       
           在泛型的使用中,有一個很容易有的誤解,那就是既然Integer是從Object派生出來的,那么ArrayList<Integer>當然就是ArrayList<Object>的子類。真的是這樣嗎?我們仔細想一想就會發現這樣做可能會帶來的問題:如果我們可以把ArrayList<Integer>向上轉型為ArrayList<Object>,那么在往這個轉了型以后的ArrayList中添加對象的時候,我們豈不是可以添加任何類型的對象(因為Object是所有對象的公共父類)?這顯然讓我們的ArrayList<Integer>失去了原本的目的。于是Java編譯器禁止我們這樣做。那既然是這樣,ArrayList<Integer>以及ArrayList<String>、ArrayList<Double>等等有沒有公共的父類呢?有,那就是ArrayList<?>。?在這里叫做通配符。我們為了縮小通配符所指代的范圍,通常也需要這樣寫:ArrayList<? extends SomeClass>,這樣寫的含義是定義這樣一個類ArrayList,比方說SomeClass有SomeExtendedClass1和SomeExtendedClass2這兩個子類,那么ArrayList<? extends SomeClass>就是如下幾個類的父類:ArrayList<SomeClass>、ArrayList<SomeExtendedClass1>和ArrayList<SomeExtendedClass2>。 

           接下來我們更進一步:既然ArrayList<? extends SomeClass>是一個通配的公用父類,那么我們可不可以往聲明為ArrayList<? extends SomeClass>的ArrayList實例中添加一個SomeExtendedClass1的對象呢?答案是不能。甚至你不能添加任何對象。為什么?因為ArrayList<? extends SomeClass>實際上代表了所有ArrayList<SomeClass>、ArrayList<SomeExtendedClass1>和ArrayList<SomeExtendedClass2>三種ArrayList,甚至包括未知的接受SomeClass其他子類對象的ArrayList。我們拿到一個定義為ArrayList<? extends SomeClass>的ArrayList的時候,我們并不能確定這個ArrayList具體是使用哪個類作為參數定義的,因此編譯器也無法讓這段代碼編譯通過。舉例來講,如果我們想往這個ArrayList中放一個SomeExtendedClass2的對象,我們如何保證它實際上不是其他的如ArrayList<SomeExtendedClass1>,而就是這個ArrayList<SomeExtendedClass2>呢?(還記得嗎?ArrayList<Integer>并非ArrayList<Object>的子類。)怎么辦?我們需要使用泛型方法。泛型方法的定義類似下面的例子:
        public static <T extends SomeClass> void add (Collection<T> c, T elem) {
        c.add(elem);
        }

      其中T代表了我們這個方法期待的那個最終的具體的類,相關的聲明必須放在方法簽名中緊靠返回類型的位置之前。在本例中,它可以是SomeClass或者SomeClass的任何子類,其說明放在void關鍵字之前(只能放在這里)。這樣我們就可以讓編譯器確信當我們試圖添加一個元素到泛型的ArrayList實例中時,可以保證類型安全。

      Java泛型的最大特點在于它是在語言級別實現的,區別于C# 2.0中的CLR級別。這樣的做法使得JRE可以不必做大的調整,缺點是無法支持一些運行時的類型甄別。一旦編譯,它就被寫死了,能提供的動態能力相當弱。

      個人認為泛型是這次J2SE(TM) 5.0中引入的最重要的語言元素,給Java語言帶來的影響也是最大。舉個例子來講,我們可以看到,幾乎所有的Collections API都被更新成支持泛型的版本。這樣做帶來的好處是顯而易見的,那就是減少代碼重復(不需要提供多個版本的某一個類或者接口以支持不同類的對象)以及增強代碼的健壯性(編譯期的類型安全檢查)。不過如何才能真正利用好這個特性,尤其是如何實現自己的泛型接口或類供他人使用,就并非那么顯而易見了。讓我們一起在使用中慢慢積累。

      1.5. 增強的for循環

      你是否已經厭倦了每次寫for循環時都要寫上那些機械的代碼,尤其當你需要遍歷數組或者Collection,如:(假設在Collection中儲存的對象是String類型的)

        public void showAll (Collection c) {
        for (Iterator iter = c.iterator(); iter.hasNext(); ) {
        System.out.println((String) iter.next());
        }
        }

        public void showAll (String[] sa) {
        for (int i = 0; i < sa.length; i++) {
        System.out.println(sa[i]);
        }
        }

      這樣的代碼不僅顯得臃腫,而且容易出錯,我想我們大家在剛開始接觸編程時,尤其是C/C++和Java,可能多少都犯過以下類似錯誤的一種或幾種:把for語句的三個表達式順序弄錯;第二個表達式邏輯判斷不正確(漏掉一些、多出一些、甚至死循環);忘記移動游標;在循環體內不小心改變了游標的位置等等。為什么不能讓編譯器幫我們處理這些細節呢?在5.0中,我們可以這樣寫:

        public void showAll (Collection c) {
        for (Object obj : c) {
        System.out.println((String) obj);
        }
        }

        public void showAll (String[] sa) {
        for (String str : sa) {
        System.out.println(str);
        }
        }

      這樣的代碼顯得更加清晰和簡潔,不是嗎?具體的語法很簡單:使用":"分隔開,前面的部分寫明從數組或Collection中將要取出的類型,以及使用的臨時變量的名字,后面的部分寫上數組或者Collection的引用。加上泛型,我們甚至可以把第一個方法變得更加漂亮:

        public void showAll (Collection<String> cs) {
        for (String str : cs) {
        System.out.println(str);
        }
        }

      有沒有發現:當你需要將Collection替換成String[],你所需要做的僅僅是簡單的把參數類型"Collection"替換成"String[]",反過來也是一樣,你不完全需要改其他的東西。這在J2SE(TM) 5.0之前是無法想象的。

      對于這個看上去相當方便的新語言元素,當你需要在循環體中訪問游標的時候,會顯得很別扭:比方說,當我們處理一個鏈表,需要更新其中某一個元素,或者刪除某個元素等等。這個時候,你無法在循環體內獲得你需要的游標信息,于是需要回退到原先的做法。不過,有了泛型和增強的for循環,我們在大多數情況下已經不用去操心那些煩人的for循環的表達式和嵌套了。畢竟,我們大部分時間都不會需要去了解游標的具體位置,我們只需要遍歷數組或Collection,對吧?

      1.6. 自動裝箱/自動拆箱

      所謂裝箱,就是把值類型用它們相對應的引用類型包起來,使它們可以具有對象的特質,如我們可以把int型包裝成Integer類的對象,或者把double包裝成Double,等等。所謂拆箱,就是跟裝箱的方向相反,將Integer及Double這樣的引用類型的對象重新簡化為值類型的數據。

      在J2SE(TM) 5.0發布之前,我們只能手工的處理裝箱和拆箱。也許你會問,為什么需要裝箱和拆箱?比方說當我們試圖將一個值類型的數據添加到一個Collection中時,就需要先把它裝箱,因為Collection的add()方法只接受對象;而當我們需要在稍后將這條數據取出來,而又希望使用它對應的值類型進行操作時,我們又需要將它拆箱成值類型的版本。現在,編譯器可以幫我們自動地完成這些必要的步驟。下面的代碼我提供兩個版本的裝箱和拆箱,一個版本使用手工的方式,另一個版本則把這些顯而易見的代碼交給編譯器去完成:

        public static void manualBoxingUnboxing(int i) {
        ArrayList<Integer> aList = new ArrayList<Integer>();
        aList.add(0, new Integer(i));
        int a = aList.get(0).intValue();
        System.out.println("The value of i is " + a);
        }

        public static void autoBoxingUnboxing(int i) {
        ArrayList<Integer> aList = new ArrayList<Integer>();
        aList.add(0, i);
        int a = aList.get(0);
        System.out.println("The value of i is " + a);
        }

      看到了吧,在J2SE(TM) 5.0中,我們不再需要顯式的去將一個值類型的數據轉換成相應的對象,從而把它作為對象傳給其他方法,也不必手工的將那個代表一個數值的對象拆箱為相應的值類型數據,只要你提供的信息足夠讓編譯器確信這些裝箱/拆箱后的類型在使用時是合法的:比方講,如果在上面的代碼中,如果我們使用的不是ArrayList而是ArrayList或者其他不兼容的版本如ArrayList,會有編譯錯誤。

      當然,你需要足夠重視的是:一方面,對于值類型和引用類型,在資源的占用上有相當大的區別;另一方面,裝箱和拆箱會帶來額外的開銷。在使用這一方便特性的同時,請不要忘記了背后隱藏的這些也許會影響性能的因素。

      1.7. 類型安全的枚舉

      在介紹J2SE(TM) 5.0中引入的類型安全枚舉的用法之前,我想先簡單介紹一下這一話題的背景。

      我們知道,在C中,我們可以定義枚舉類型來使用別名代替一個集合中的不同元素,通常是用于描述那些可以歸為一類,而又具備有限數量的類別或者概念,如月份、顏色、撲克牌、太陽系的行星、五大洲、四大洋、季節、學科、四則運算符,等等。它們通常看上去是這個樣子:

      typedef enum {SPRING, SUMMER, AUTUMN, WINTER} season;

      實質上,這些別名被處理成int常量,比如0代表SPRING,1代表SUMMER,以此類推。因為這些別名最終就是int,于是你可以對它們進行四則運算,這就造成了語意上的不明確。

      Java一開始并沒有考慮引入枚舉的概念,也許是出于保持Java語言簡潔的考慮,但是使用Java的廣大開發者對于枚舉的需求并沒有因為Java本身沒有提供而消失,于是出現了一些常見的適用于Java的枚舉設計模式,如int enum和typesafe enum,還有不少開源的枚舉API和不開源的內部實現。

      我大致說一下int enum模式和typesafe enum模式。所謂int enum模式就是模仿C中對enum的實現,如:

        public class Season {
        public static final int SPRING = 0;
        public static final int SUMMER = 1;
        public static final int AUTUMN = 2;
        public static final int WINTER = 3;
        }
        這種模式跟C中的枚舉沒有太多本質上的區別,C枚舉的局限它基本上也有。而typesafe enum模式則要顯得健壯得多:
        public class Season {
        private final String name;
        private Season(String name) {
        this.name = name;
        }
        public String toString() {
        return name;
        }
        public static final Season SPRING = new Season("spring");
        public static final Season SUMMER = new Season("summer");
        public static final Season AUTUMN = new Season("autumn");
        public static final Season WINTER = new Season("winter");
        }

      后一種實現首先通過私有的構造方法阻止了對該類的繼承和顯式實例化,因而我們只可能取得定義好的四種Season類別,并且提供了方便的toString()方法獲取有意義的說明,而且由于這是一個完全意義上的類,所以我們可以很方便的加入自己的方法和邏輯來自定義我們的枚舉類。

      最終,Java決定擁抱枚舉,在J2SE(TM) 5.0中,我們看到了這一變化,它所采用的設計思路基本上就是上面提到的typesafe enum模式。它的語法很簡單,用一個實際的例子來說,要定義一個枚舉,我們可以這樣寫:

      public enum Language {CHINESE, ENGLISH, FRENCH, HUNGARIAN}

      接下來我們就可以通過Language.ENGLISH來使用了。呃…這個例子是不是有點太小兒科了,我們來看一個復雜點的例子。使用Java的類型安全枚舉,我們可以為所有枚舉元素定義公用的接口,然后具體到每個元素本身,可以針對這些接口實現一些特定的行為。這對于那些可以歸為一類,又希望能通過統一的接口訪問的不同操作,將會相當方便。通常,為了實現類似的功能,我們需要自己來維護一套繼承關系或者類似的枚舉模式。這里借用Java官方網站上的一個例子:

        public enum Operation {
        PLUS { double eval(double x, double y) { return x + y; } },
        MINUS { double eval(double x, double y) { return x - y; } },
        TIMES { double eval(double x, double y) { return x * y; } },
        DIVIDE { double eval(double x, double y) { return x / y; } };

        // Do arithmetic op represented by this constant
        abstract double eval(double x, double y);
        }
        在這個枚舉中,我們定義了四個元素,分別對應加減乘除四則運算,對于每一種運算,我們都可以調用eval()方法,而具體的方法實現各異。我們可以通過下面的代碼來試驗上面這個枚舉類:
        public static void main(String args[]) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        for (Operation op : Operation.values()) {
        System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y));
        }
        }

      怎么樣,使用枚舉,我們是不是能夠很方便的實現一些有趣的功能?其實說穿了,Java的類型安全枚舉就是包含了有限數量的已生成好的自身實例的一種類,這些現成的實例可以通過類的靜態字段來獲取。

      1.8. 可變長度參數

      顧名思義,可變長度參數就是指在方法的參數體中,只要定義恰當,我們可以使用任意數量的參數,類似于使用數組。在J2SE(TM) 5.0中,一個新的語法被引入,就是在參數類型名稱后面加上"...",表示該方法可以接受多個該類型的參數。需要說明的是可變長度參數必須放在參數列表的最后,且一個方法只能包含一個這樣的參數。在方法體內部,這樣的參數被當作數組處理,看上去代碼應該類似這個樣子:

        public String testVararg(String... args) {
        StringBuilder sb = new StringBuilder();
        for (String str : args) {
        sb.append(str);
        }
        return sb.toString();
        }

      這樣的方法簽名跟你寫成testVararg(String[] args)的區別在于:在調用時,你不再需要傳入一個包裝好的String數組,你只需要簡單的寫一連串String參數,以逗號隔開即可,就如同這個方法正好有一個重載的版本是接受那么多個String參數一樣。

      1.9. 靜態引入

      所謂靜態引入就是指除了引入類之外,我們現在又多了一種選擇:引入某個類的靜態字段。如:

      import static java.lang.Math.PI;

      或者

      import static java.lang.Math.*;

      這樣我們在接下來的代碼中,當我們需要使用某個被引入的靜態字段時,就不用再寫上前面的類名了。當然,出現名字沖突時,跟原來的類引入一樣,還是需要前綴以示區分。我個人認為這個新語言元素意義不大。當引入太多靜態字段后,代碼會變得難以閱讀和維護。由于靜態字段的名字通常不如類名那么具有描述性,我認為原先在靜態字段前寫上類名才是更好的選擇。不過,畢竟每個人的喜好和需求不同,如果你覺得它對你有用,既然提供了,那么就用咯。

      1.10. 元數據(注解)

      注解是J2SE(TM) 5.0引入的重要語言元素,它所對應的JSR是JSR 175,我們先來看看JSR 175的文檔對注解的說明:

      注解不會直接影響程序的語義,而開發和部署工具則可以讀取這些注解信息,并作相應處理,如生成額外的Java源代碼、XML文檔、或者其他將與包含注解的程序一起使用的物件。

      在之前的J2SE版本中,我們已經使用到了一部分早期的注解元素,如@deprecated等。這些元素通常被用于產生HTML的Javadoc。在J2SE(TM) 5.0中,注解被正式引入,且推到了Java歷史上前所未有的高度。

      現在,注解不僅僅被用來產生Javadoc,更重要的,注解使得代碼的編譯期檢查更加有效和方便,同時也增強了代碼的描述能力。有一些注解是隨著J2SE(TM) 5.0一起發布的,我們可以直接使用。除此之外,我們也可以很方便的實現自定義的注解。在此基礎上,很多以前我們只能靠反射機制來完成的功能也變得更加容易實現。

      我們來看現成的有哪些有用的注解:

      首先是@Override,這個注解被使用在方法上,表明這個方法是從其父類繼承下來的,這樣的寫法可以很方便的避免我們在重寫繼承下來的方法時,不至于不小心寫錯了方法簽名,且悄悄的溜過了編譯器,造成隱蔽性相當高的bug。

      其次是@Deprecated,表明該項(類、字段、方法)不再被推薦使用。

      還有一個@SuppressWarnings,表明該項(類、字段、方法)所涵蓋的范圍不需要顯示所有的警告信息。這個注解需要提供參數,如unchecked等等。

      下面我通過一個例子向大家說明這些現成的注解的用法:

        public class Main {
        @Deprecated
        public String str;
        public static void main(String[] args) {
        new SubMain().doSomething();
        }
        public void doSomething() {
        System.out.println("Done.");
        }
        }

        class SubMain extends Main {
        @Override
        @SuppressWarnings("unchecked", "warning")
        public void doSomething() {
        java.util.ArrayList aList = new java.util.ArrayList();
        aList.add(new Integer(0));
        System.out.println("Done by SubMain.");
        }
        }

      當然,我們也完全可以寫自己的注解。注解定義的語法是@interface關鍵字。J2SE(TM) 5.0支持三種形式的注解:不帶參數的標記注解、帶一個參數的注解和帶多個參數的完整注解。下面分別舉例說明:

        標記注解,類似@Deprecated,如:
        @interface SomeEmptyAnnotation {}
        單個參數的注解,如:
        @interface MySingleElementAnnotation {
        String value();
        }
        以及多個參數的注解,如:
        @interface MyAnnotationForMethods {
        int index();
        String info();
        String developer() default "Sean GAO";
        }

      我們可以看到,注解的定義跟interface的定義相當類似,我們還可以指定默認值。對于這些注解,我們也可以為其添加注解,所謂“注解的注解”。比方講,我們通常會使用@Target指定注解的作用對象,以及用@Retention指定注解信息寫入的級別,如源代碼、類文件等等。舉個例子:

        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.SOURCE)
        public @interface SignedMethod {
        }
        在使用時,我們需要在注解名稱前面寫上@,然后()中指定參數值,如:
        @MyAnnotationForMethods (
        index = 1,
        info = "This is a method to test MyAnnotation.",
        developer = "Somebody else"
        )
        public void testMethod1() {
        // ...
        }

      注解的最大作用在于它在源代碼的基礎上增加了有用的信息,使得源代碼的描述性更強。這些信息可以被代碼之外的工具識別,從而可以很方便的增加外部功能,以及減少不必要的相關代碼/文件的維護。這里我想簡單提一個超出J2SE(TM) 5.0范疇的話題:在未來的EJB 3.0規范中會有相當多的對注解的應用,讓我們預覽一下將來的無狀態會話bean用注解來定義會是什么樣子:

        @Stateless public class BookShelfManagerBean {
        public void addBook(Book aBook) {
        // business logic goes here...
        }
        public Collection getAllBooks() {
        // business logic goes here...
        }
        // ...
        }

      我們甚至不用寫任何接口和部署描述符,這些工作將完全由外部工具通過讀取注解加上反射來完成,這不是很好嗎?

      1.11. C風格格式化輸出

      Java總算也有類似C的printf()風格的方法了,方法名同樣叫作printf(),這一特性依賴于前邊提到的可變長度參數。舉個例子來說,我們現在可以寫:

      System.out.printf("%s has a value of %d.%n", someString, a);

      怎么樣,看上去還不錯吧?需要注意的是Java為了支持多平臺,新增了%n標示符,作為對\n的補充。有關Java格式化輸出的具體語法,請參考java.util.Formatter的API文檔。

      1.12. 結語

      在這一篇介紹性的文章中,我們一起領略了J2SE 5.0帶來的新的語言元素,不知道大家是否也跟筆者一樣,感受到了這些新特性在提高我們的開發效率上所作的巨大努力。其實不只是語言元素,J2SE(TM) 5.0的發布在其他很多方面都作了不小的改進,包括虛擬機、新的API類庫等等,性能和功能上都有大幅提升。

      對于主要靠J2EE吃飯的朋友來講,也許真正意義上要在工作中充分利用這些新的元素,恐怕要等主流的J2EE服務器都支持J2EE(TM) 5.0的那一天了,對此我充滿期待。

    posted @ 2010-05-16 15:35 redcoatjk 閱讀(159) | 評論 (0)編輯 收藏
    僅列出標題
    共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 
    CALENDER
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(3)

    隨筆分類(22)

    隨筆檔案(76)

    文章分類(12)

    文章檔案(17)

    搜索

    •  

    積分與排名

    • 積分 - 250517
    • 排名 - 227

    最新評論

    評論排行榜


    Powered By: 博客園
    模板提供滬江博客

    主站蜘蛛池模板: 亚洲精品美女在线观看| 亚洲精品天堂在线观看| 亚洲国产精品综合久久网络| 亚洲avav天堂av在线不卡| 国产产在线精品亚洲AAVV| 免费看又黄又无码的网站| www.亚洲一区| 亚洲熟妇无码八V在线播放| 免费高清A级毛片在线播放| 一级毛片试看60分钟免费播放| 18国产精品白浆在线观看免费| 亚洲色婷婷六月亚洲婷婷6月| 亚洲AV成人无码网天堂| 免费播放在线日本感人片| 亚欧人成精品免费观看| 亚洲啪啪综合AV一区| 美女被免费网站在线视频免费| 免费影院未满十八勿进网站| 亚洲电影免费在线观看| 久久九九免费高清视频| 国产成人aaa在线视频免费观看 | 亚洲四虎永久在线播放| 亚洲一区二区三区免费| 免费在线黄色网址| 亚洲色精品三区二区一区| 91精品免费国产高清在线| 亚洲成色999久久网站| 最近中文字幕免费大全| 亚洲精品国产精品乱码不卞 | 亚洲精品私拍国产福利在线| 国产免费A∨在线播放| 免费人成视频在线观看视频| 亚洲精华国产精华精华液网站| 成人免费观看一区二区| 7777久久亚洲中文字幕蜜桃| 免费国产在线视频| 亚洲2022国产成人精品无码区| 国产福利免费视频 | 亚洲欧洲国产精品你懂的| 青柠影视在线观看免费高清| 黑人大战亚洲人精品一区|