隨筆:93 文章:11 評論:22 引用:0
          首頁 發(fā)新隨筆
          發(fā)新文章 聯(lián)系 聚合管理

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

          JNDI的一篇文章

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

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

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

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

          沒有JNDI的做法: 

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

          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. }  



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

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

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

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

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

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

          具體操作如下(以JBoss為例): 
          1、配置數(shù)據(jù)源 
          在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夾下面,有很多不同數(shù)據(jù)庫引用的數(shù)據(jù)源定義模板。將其中的 mysql-ds.xml 文件Copy到你使用的服務器下,如 D:\jboss420GA\server\default\deploy。 
          修改 mysql-ds.xml 文件的內(nèi)容,使之能通過JDBC正確訪問你的MySQL數(shù)據(jù)庫,如下: 
          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的數(shù)據(jù)源,其參數(shù)包括JDBC的URL,驅動類名,用戶名及密碼等。 

          2、在程序中引用數(shù)據(jù)源: 

          Java代碼 
          1. Connection conn=null;  
          2. try {  
          3.   Context ctx=new InitialContext();  
          4.   Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用數(shù)據(jù)源  
          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引用數(shù)據(jù)源的編程代碼量相差無幾,但是現(xiàn)在的程序可以不用關心具體JDBC參數(shù)了。//解藕了,可擴展了 
          在系統(tǒng)部署后,如果數(shù)據(jù)庫的相關參數(shù)變更,只需要重新配置 mysql-ds.xml 修改其中的JDBC參數(shù),只要保證數(shù)據(jù)源的名稱不變,那么程序源代碼就無需修改。 

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

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

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

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


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

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


          從上面的文章中可以看出: 
          1、JNDI 提出的目的是為了解藕,是為了開發(fā)更加容易維護,容易擴展,容易部署的應用。 
          2、JNDI 是一個sun提出的一個規(guī)范(類似于jdbc),具體的實現(xiàn)是各個j2ee容器提供商,sun   只是要求,j2ee容器必須有JNDI這樣的功能。 
          3、JNDI 在j2ee系統(tǒng)中的角色是“交換機”,是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通過綁定的概念將對象和名稱聯(lián)系起來。在一個文件系統(tǒng)中,文件名被綁定給文件。在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列表出命名空間的特殊級別的內(nèi)容 
          如: 
          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 閱讀(420) | 評論 (0)編輯 收藏
           
          各種數(shù)字類型轉換成字符串型:

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

          字符串型轉換成各種數(shù)字類型:

          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 );

          數(shù)字類型與數(shù)字類對象之間的轉換:

          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 閱讀(212) | 評論 (0)編輯 收藏
           
          1. 觸發(fā)器使用教程和命名規(guī)范  
          2.   
          3.   
          4. 目  錄  
          5. 觸發(fā)器使用教程和命名規(guī)范    1  
          6. 1,觸發(fā)器簡介 1  
          7. 2,觸發(fā)器示例 2  
          8. 3,觸發(fā)器語法和功能  3  
          9. 4,例一:行級觸發(fā)器之一    4  
          10. 5,例二:行級觸發(fā)器之二    4  
          11. 6,例三:INSTEAD OF觸發(fā)器  6  
          12. 7,例四:語句級觸發(fā)器之一   8  
          13. 8,例五:語句級觸發(fā)器之二   9  
          14. 9,例六:用包封裝觸發(fā)器代碼  10  
          15. 10,觸發(fā)器命名規(guī)范  11  
          16.   
          17. 1,觸發(fā)器簡介  
          18. 觸發(fā)器(Trigger)是數(shù)據(jù)庫對象的一種,編碼方式類似存儲過程,與某張表(Table)相關聯(lián),當有DML語句對表進行操作時,可以引起觸發(fā)器的執(zhí)行,達到對插入記錄一致性,正確性和規(guī)范性控制的目的。在當年C/S時代盛行的時候,由于客戶端直接連接數(shù)據(jù)庫,能保證數(shù)據(jù)庫一致性的只有數(shù)據(jù)庫本身,此時主鍵(Primary Key),外鍵(Foreign Key),約束(Constraint)和觸發(fā)器成為必要的控制機制。而觸發(fā)器的實現(xiàn)比較靈活,可編程性強,自然成為了最流行的控制機制。到了B/S時代,發(fā)展成4層架構,客戶端不再能直接訪問數(shù)據(jù)庫,只有中間件才可以訪問數(shù)據(jù)庫。要控制數(shù)據(jù)庫的一致性,既可以在中間件里控制,也可以在數(shù)據(jù)庫端控制。很多的青睞Java的開發(fā)者,隨之將數(shù)據(jù)庫當成一個黑盒,把大多數(shù)的數(shù)據(jù)控制工作放在了Servlet中執(zhí)行。這樣做,不需要了解太多的數(shù)據(jù)庫知識,也減少了數(shù)據(jù)庫編程的復雜性,但同時增加了Servlet編程的工作量。從架構設計來看,中間件的功能是檢查業(yè)務正確性和執(zhí)行業(yè)務邏輯,如果把數(shù)據(jù)的一致性檢查放到中間件去做,需要在所有涉及到數(shù)據(jù)寫入的地方進行數(shù)據(jù)一致性檢查。由于數(shù)據(jù)庫訪問相對于中間件來說是遠程調(diào)用,要編寫統(tǒng)一的數(shù)據(jù)一致性檢查代碼并非易事,一般采用在多個地方的增加類似的檢查步驟。一旦一致性檢查過程發(fā)生調(diào)整,勢必導致多個地方的修改,不僅增加工作量,而且無法保證每個檢查步驟的正確性。觸發(fā)器的應用,應該放在關鍵的,多方發(fā)起的,高頻訪問的數(shù)據(jù)表上,過多使用觸發(fā)器,會增加數(shù)據(jù)庫負擔,降低數(shù)據(jù)庫性能。而放棄使用觸發(fā)器,則會導致系統(tǒng)架構設計上的問題,影響系統(tǒng)的穩(wěn)定性。  
          19.   
          20.   
          21. 2,觸發(fā)器示例  
          22. 觸發(fā)器代碼類似存儲過程,以PL/SQL腳本編寫。下面是一個觸發(fā)器的示例:  
          23. 新建員工工資表salary  
          24. create table SALARY  
          25. (  
          26.   EMPLOYEE_ID NUMBER, --員工ID  
          27.   MONTH       VARCHAR2(6), --工資月份  
          28.   AMOUNT      NUMBER --工資金額  
          29. )  
          30.   
          31. 創(chuàng)建與salary關聯(lián)的觸發(fā)器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(‘觸發(fā)器已被執(zhí)行’);  
          41. 10   End;  
          42. 打開一個SQL Window窗口(使用PL/SQL Developer工具),或在sqlplus中輸入:  
          43. Insert into salary(employee_id, month, amount) values(1, ‘200606’, 10000);  
          44. 執(zhí)行后可以在sqlplus中,或在SQL Window窗口的Output中見到  
          45. 員工ID:1  
          46. 工資月份:200606  
          47. 工資:10000  
          48. 觸發(fā)器已執(zhí)行  
          49.   
          50. 在代碼的第一行,定義了數(shù)據(jù)庫對象的類型是trigger,定義觸發(fā)器的名稱是salary_trg_rai  
          51. 第二行說明了這是一個after觸發(fā)器,在DML操作實施之后執(zhí)行。緊接著的insert說明了這是一個針對insert操作的觸發(fā)器,每個對該表進行的insert操作都會執(zhí)行這個觸發(fā)器。  
          52. 第三行說明了這是一個針對行級的觸發(fā)器,當插入的記錄有n條時,在每一條插入操作時都會執(zhí)行該觸發(fā)器,總共執(zhí)行n次。  
          53. Declare后面跟的是本地變量定義部分,如果沒有本地變量定義,此部分可以為空  
          54. Begin和end括起來的代碼,是觸發(fā)器的執(zhí)行部分,一般會對插入記錄進行一致性檢查,在本例中打印了插入的記錄和“觸發(fā)器已執(zhí)行”。  
          55. 其中:new對象表示了插入的記錄,可以通過:new.column_name來引用記錄的每個字段值  
          56.   
          57.   
          58. 3,觸發(fā)器語法和功能  
          59. 觸發(fā)器的語法如下  
          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.     --觸發(fā)器代碼  
          67. END;  
          68.   
          69. Trigger_name 是觸發(fā)器的名稱。<before | after | instead of>可以選擇before或者after或instead of。 Before表示在DML語句實施前執(zhí)行觸發(fā)器,而after表示在在dml語句實施之后執(zhí)行觸發(fā)器,instead of觸發(fā)器用在對視圖的更新上。<insert | update | delete>可以選擇一個或多個DML語句,如果選擇多個,則用or分開,如:insert or update。Table_name是觸發(fā)器關聯(lián)的表名。  
          70. [FOR EACH ROW]為可選項,如果注明了FOR EACH ROW,則說明了該觸發(fā)器是一個行級的觸發(fā)器,DML語句處理每條記錄都會執(zhí)行觸發(fā)器;否則是一個語句級的觸發(fā)器,每個DML語句觸發(fā)一次。  
          71. WHEN后跟的condition是觸發(fā)器的響應條件,只對行級觸發(fā)器有效,當操作的記錄滿足condition時,觸發(fā)器才被執(zhí)行,否則不執(zhí)行。Condition中可以通過new對象和old對象(注意區(qū)別于前面的:new和:old,在代碼中引用需要加上冒號)來引用操作的記錄。  
          72. 觸發(fā)器代碼可以包括三種類型:未涉及數(shù)據(jù)庫事務代碼,涉及關聯(lián)表(上文語法中的table_name)數(shù)據(jù)庫事務代碼,涉及除關聯(lián)表之外數(shù)據(jù)庫事務代碼。其中第一種類型代碼只對數(shù)據(jù)進行簡單運算和判斷,沒有DML語句,這種類型代碼可以在所有的觸發(fā)器中執(zhí)行。第二種類型代碼涉及到對關聯(lián)表的數(shù)據(jù)操作,比如查詢關聯(lián)表的總記錄數(shù)或者往關聯(lián)表中插入一條記錄,該類型代碼只能在語句級觸發(fā)器中使用,如果在行級觸發(fā)器中使用,將會報ORA-04091錯誤。第三種類型代碼涉及到除關聯(lián)表之外的數(shù)據(jù)庫事務,這種代碼可以在所有觸發(fā)器中使用。  
          73.   
          74. 從觸發(fā)器的功能上來看,可以分成3類:  
          75. ?   重寫列(僅限于before觸發(fā)器)  
          76. ?   采取行動(任何觸發(fā)器)  
          77. ?   拒絕事務(任何觸發(fā)器)  
          78. “重寫列”用于對表字段的校驗,當插入值為空或者插入值不符合要求,則觸發(fā)器用缺省值或另外的值代替,在多數(shù)情況下與字段的default屬性相同。這種功能只能在行級before觸發(fā)器中執(zhí)行。“采取行動”針對當前事務的特點,對相關表進行操作,比如根據(jù)當前表插入的記錄更新其他表,銀行中的總帳和分戶帳間的總分關系就可以通過這種觸發(fā)器功能來維護。“拒絕事務”用在對數(shù)據(jù)的合法性檢驗上,當更新的數(shù)據(jù)不滿足表或系統(tǒng)的一致性要求,則通過拋出異常的方式拒絕事務,在其上層的代碼可以捕獲這個異常并進行相應操作。  
          79.   
          80. 下面將通過舉例說明,在例子中將觸發(fā)器主體的語法一一介紹,讀者可以在例子中體會觸發(fā)器的功能。  
          81.   
          82. 4,例一:行級觸發(fā)器之一  
          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的行級觸發(fā)器。在第二行中of amount on salary的意思是只有當amount列被更新時,update觸發(fā)器才會有效。所以,以下語句將不會執(zhí)行觸發(fā)器:  
          94. Update salary set month = ‘200601’ where month = ‘200606’;  
          95. 在觸發(fā)器主體的if語句表達式中,inserting, updating和deleting可以用來區(qū)分當前是在做哪一種DML操作,可以作為把多個類似觸發(fā)器合并在一個觸發(fā)器中判別觸發(fā)事件的屬性。  
          96.   
          97. 5,例二:行級觸發(fā)器之二  
          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字段代表該員工每月所能分配的最高工資。下面的觸發(fā)器根據(jù)插入或修改記錄的 employee_id,在employment表中查到該員工的每月最高工資,如果插入或修改后的amount超過這個值,則報錯誤。  
          125. 代碼中的when子句表明了該觸發(fā)器只針對修改或插入后的amount值超過1000,而修改前的amount值小于500的記錄。New對象和old對象分別表示了操作前和操作后的記錄對象。對于insert操作,由于當前操作記錄無歷史對象,所以old對象中所有屬性是null;對于delete操作,由于當前操作記錄沒有更新對象,所以new對象中所有屬性也是null。但在這兩種情況下,并不影響old和new對象的引用和在觸發(fā)器主體中的使用,和普通的空值作同樣的處理。  
          126. 在觸發(fā)器主體中,先通過:new.employee_id,得到該員工的工資上限,然后在if語句中判斷更新后的員工工資是否超限,如果超限則錯誤代碼為-20000,錯誤信息為“工資超限”的自定義錯誤。其中的raise_application_error包含兩個參數(shù),前一個是自定義錯誤代碼,后一個是自定義錯誤代碼信息。其中自定義錯誤代碼必須小于或等于-20000。執(zhí)行完該語句后,一個異常被拋出,如果在上一層有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. 執(zhí)行后,將在output中或者sqlplus窗口中見著以下信息:  
          140. -20000  
          141. ORA-20000: 工資超出限制  
          142. ORA-06512: 在"SCOTT.SALARY_RAI", line 9  
          143. ORA-04088: 觸發(fā)器 'SCOTT.SALARY_RAI' 執(zhí)行過程中出錯  
          144.   
          145. 這里的raise_application_error相當于拒絕了插入或者修改事務,當上層代碼接受到這個異常后,判斷該異常代碼等于-20000,可以作出回滾事務或者繼續(xù)其他事務的處理。  
          146.   
          147. 以上兩個例子中用到的inserting, updating, deleting和raise_application_error都是dbms_standard包中的函數(shù),具體的說明可以參照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行級觸發(fā)器,:new和:old對象的屬性值都是一樣的,主要是對于在Oracle約束(Constraint)之前或之后的執(zhí)行觸發(fā)器的選擇。需要注意的是,可以在before行觸發(fā)器中更改:new對象中的值,但是在after行觸發(fā)器就不行。  
          157.   
          158. 下面介紹一種instead of觸發(fā)器,該觸發(fā)器主要使用在對視圖的更新上,以下是instead of觸發(fā)器的語法:  
          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.     --觸發(fā)器代碼  
          166. END;  
          167.   
          168. 其他部分語法同前面所述的before和after語法是一樣的,唯一不同的是在第二行用上了instead of關鍵字。對于普通的視圖來說,進行 insert等操作是被禁止的,因為Oracle無法知道操作的字段具體是哪個表中的字段。但我們可以通過建立instead of觸發(fā)器,在觸發(fā)器主體中告訴Oracle應該更新,刪除或者修改哪些表的哪部分字段。如:  
          169.   
          170. 6,例三:instead of觸發(fā)器  
          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. 如果執(zhí)行插入語句  
          178. INSERT INTO employee_salary(employee_id, maxsalary, MONTH, amount)  
          179. VALUES(10100000'200606'10000);  
          180. 系統(tǒng)會報錯:  
          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. 該觸發(fā)器被建立后,執(zhí)行上述insert操作,系統(tǒng)就會提示成功插入一條記錄。  
          216. 但需要注意的是,這里的“成功插入一條記錄”,只是Oracle并未發(fā)現(xiàn)觸發(fā)器中有異常拋出,而根據(jù)insert語句中涉及的記錄數(shù)作出一個判斷。若觸發(fā)器的主體什么都沒有,只是一個空語句,Oracle也會報“成功插入一條記錄”。同樣道理,即使在觸發(fā)器主體里往多個表中插入十條記錄,Oracle的返回也是“成功插入一條記錄”。  
          217.   
          218.   
          219.   
          220.   
          221. 行級觸發(fā)器可以解決大部分的問題,但是如果需要對本表進行掃描檢查,比如要檢查總的工資是否超限了,用行級觸發(fā)器是不行的,因為行級觸發(fā)器主體中不能有涉及到關聯(lián)表的事務,這時就需要用到語句級觸發(fā)器。以下是語句級觸發(fā)器的語法:  
          222. CREATE OR REPLACE TRIGGER trigger_name  
          223. <before | after | instead of ><insert | update | delete > ON table_name  
          224. DECLARE  
          225. BEGIN  
          226.     --觸發(fā)器主體  
          227. END;  
          228.   
          229. 從語法定義上來看,行級觸發(fā)器少了for each row,也不能使用when子句來限定入口條件,其他部分都是一樣的,包括insert, update, delete和instead of都可以使用。  
          230.   
          231.   
          232. 7,例四:語句級觸發(fā)器之一  
          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. 以上代碼定義了一個語句級觸發(fā)器,該觸發(fā)器檢查在insert和update了amount字段后操作后,工資表中所有工資記錄累加起來是否超過500000,如果超過則拋出異常。從這個例子可以看出,語句級觸發(fā)器可以對關聯(lián)表表進行掃描,掃描得到的結果可以用來作為判斷一致性的標志。需要注意的是,在 before語句觸發(fā)器主體和after語句觸發(fā)器主體中對關聯(lián)表進行掃描,結果是不一樣的。在before語句觸發(fā)器主體中掃描,掃描結果將不包括新插入和更新的記錄,也就是說當以上代碼換成 before觸發(fā)器后,以下語句將不報錯:  
          245. INSERT INTO salary(employee_id, month, amount) VALUEs(2'200601'600000)  
          246. 這是因為在主體中得到的v_sumsalary并不包括新插入的600000工資。  
          247. 另外,在語句級觸發(fā)器中不能使用:new和:old對象,這一點和行級觸發(fā)器是顯著不同的。如果需要檢查插入或更新后的記錄,可以采用臨時表技術。  
          248. 臨時表是一種Oracle數(shù)據(jù)庫對象,其特點是當創(chuàng)建數(shù)據(jù)的進程結束后,進程所創(chuàng)建的數(shù)據(jù)也隨之清除。進程與進程不可以互相訪問同一臨時表中對方的數(shù)據(jù),而且對臨時表進行操作也不產(chǎn)生undo日志,減少了數(shù)據(jù)庫的消耗。具體有關臨時表的知識,可以參看有關書籍。  
          249. 為了在語句級觸發(fā)器中訪問新插入后修改后的記錄,可以增加行級觸發(fā)器,將更新的記錄插入臨時表中,然后在語句級觸發(fā)器中掃描臨時表,獲得修改后的記錄。臨時表的表結構一般與關聯(lián)表的結構一致。  
          250.   
          251.   
          252. 8,例五:語句級觸發(fā)器之二  
          253. 目的:限制每個員工的總工資不能超過50000,否則停止對該表操作。  
          254. 創(chuàng)建臨時表  
          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. 為了把操作記錄插入到臨時表中,創(chuàng)建行級觸發(fā)器:  
          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. 該觸發(fā)器的作用是把更新后的記錄信息插入到臨時表中,如果更新了多條記錄,則每條記錄都會保存在臨時表中。  
          272.   
          273. 創(chuàng)建語句級觸發(fā)器:  
          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. 該觸發(fā)器首先用游標從salary_tmp臨時表中逐條讀取更新或插入的記錄,取employee_id,在關聯(lián)表salary中查找所有相同員工的工資記錄,并求和。若某員工工資總和超過50000,則拋出異常。如果檢查通過,則清空臨時表,避免下次檢查相同的記錄。  
          292. 執(zhí)行以下語句:  
          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. 在執(zhí)行第三句時系統(tǒng)報錯:  
          297. ORA-20002:員工累計工資超過50000  
          298. 查詢salary表,發(fā)現(xiàn)前兩條記錄正常插入了,第三條記錄沒有插入。  
          299.   
          300.   
          301. 如果系統(tǒng)結構比較復雜,而且觸發(fā)器的代碼比較多,在觸發(fā)器主體中寫過多的代碼,對于維護來說是一個困難。這時可以將所有觸發(fā)器的代碼寫到同一個包中,不同的觸發(fā)器代碼以不同的存儲過程封裝,然后觸發(fā)器主體中調(diào)用這部分代碼。  
          302.   
          303. 9,例六:用包封裝觸發(fā)器代碼  
          304. 目的:改寫例五,封裝觸發(fā)器主體代碼  
          305. 創(chuàng)建代碼包:  
          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用于在行級觸發(fā)器中調(diào)用,往salary_tmp臨時表中裝載更新或插入記錄。而check_salary用于在語句級觸發(fā)器中檢查員工累計工資是否超限。  
          331.   
          332. 修改行級觸發(fā)器和語句級觸發(fā)器:  
          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中,觸發(fā)器主體中只實現(xiàn)了一個調(diào)用功能。  
          347.   
          348. 10,觸發(fā)器命名規(guī)范  
          349. 為了方便對觸發(fā)器命名和根據(jù)觸發(fā)器名稱了解觸發(fā)器含義,需要定義觸發(fā)器的命名規(guī)范:  
          350. Trigger_name = table_name_trg_<R|S><A|B|I><I|U|D>  
          351.   
          352. 觸發(fā)器名限于30個字符。必須縮寫表名,以便附加觸發(fā)器屬性信息。  
          353. <R|S>基于行級(row)還是語句級(statement)的觸發(fā)器  
          354. <A|B|I>after, before或者是instead of觸發(fā)器  
          355. <I|U|D>觸發(fā)事件是insert,update還是delete。如果有多個觸發(fā)事件則連著寫  
          356.   
          357. 例如:  
          358. Salary_rai      salary表的行級after觸發(fā)器,觸發(fā)事件是insert  
          359. Employee_sbiud  employee表的語句級before觸發(fā)器,觸發(fā)事件是insert,update和delete  
          posted @ 2010-08-04 15:27 redcoatjk 閱讀(327) | 評論 (0)編輯 收藏
           

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

           

          apache支持靜態(tài)頁,tomcat支持動態(tài)的,比如servlet等,


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

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

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

           

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

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

           

           

          二:

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

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

           

           

          三:

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

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

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

          On 表名

          [for each row]

          When 條件

          PL/SQL塊

          說明:

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

          When條件的出現(xiàn)說明了,在DML操作的時候也許一定會觸發(fā)觸發(fā)器,但是觸發(fā)器不一定會做實際的工作,比如when 后的條件不為真的時候,觸發(fā)器只是簡單地跳過了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 只出現(xiàn)在insert和update時,old只出現(xiàn)在update和delete時。在insert時new表示新插入的行數(shù)據(jù),update時new 表示要替換的新數(shù)據(jù)、old表示要被更改的原來的數(shù)據(jù)行,delete時old表示要被刪除的數(shù)據(jù)。

          注意:

          在觸發(fā)器中不能使用commit。



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

          區(qū)別于SQL的其它函數(shù),DECODE函數(shù)還能識別和操作空值。

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

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

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

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

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

          例如:

          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()函數(shù)一起使用。

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

          同樣,也可以用CASE實現(xiàn):

          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列。要求按照:語、數(shù)、外的順序進行排序。這時,就可以非常輕松的使用 Decode完成要求了。

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

          Unique約束

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

          創(chuàng)建語句:
            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 閱讀(363) | 評論 (0)編輯 收藏
           
          作為開源的連接池Proxool
          有以下優(yōu)點。
          透明性   可以明的添加接連池而不影響你原來的項目的JDBC代碼;
          開放性 你可以方便的與其它的開源產(chǎn)品進行整合。如hibernate  中自帶的這個Proxool
          標準性 它是在J2SE下開出來的。你可以放心的開發(fā)
          易用性  非常容易 的進行配置。
          proxool是一個非常強大的連接池工具包,我覺得相比dbcp、c3p0這兩個連接池包都要好用,我用loadrunner測試過,這三個連 接池的從性能上排名如下:proxool>c3p0>dbcp,特別是dbcp在大并發(fā)的情況下總是出現(xiàn)各種異常。

          下面是實現(xiàn)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只能管理由自己產(chǎn)生的連接-->  
                  <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自動偵察各個連接狀態(tài)的時間間隔(毫秒),偵察到空閑的連接就馬上回收,超時的銷毀-->  
                  <house-keeping-sleep-time>90000</house-keeping-sleep-time>  
                  <!-- 指因未有空閑連接可以分配而在隊列中等候的最大請求數(shù),超過這個請求數(shù)的用戶連接就不會被接受-->    
                  <maximum-new-connections>150</maximum-new-connections>  
                  <!-- 最少保持的空閑連接數(shù)-->    
                  <prototype-count>3</prototype-count>  
                  <!-- 允許最大連接數(shù),超過了這個連接,再有請求時,就排在隊列中等候,最大的等待請求數(shù)由maximum-new-connections決定-->    
                  <maximum-connection-count>100</maximum-connection-count>  
                  <!-- 最小連接數(shù)-->  
                  <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提供的管理監(jiān)控工具,可查看當前數(shù)據(jù)庫連接情況。如果運行不成功,請刪除本行 -->
          <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>  


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


          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();//獲得活動連接數(shù)        
                      int availableCount=snapshot.getAvailableConnectionCount();//獲得可得到的連接數(shù)        
                      int maxCount=snapshot.getMaximumConnectionCount() ;//獲得總連接數(shù)        
                      if(curActiveCount!=activeCount)//當活動連接數(shù)變化時輸出的信息        
                      {        
                       System.out.println("活動連接數(shù):"+curActiveCount+"(active)  可得到的連接數(shù):"+availableCount+"(available)  總連接數(shù):"+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進行大并發(fā)的測試,性能還是很好的。

          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的數(shù)據(jù)源。


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

            · 泛型

            · 增強的for循環(huán)

            · 自動裝箱和自動拆箱

            · 類型安全的枚舉

            · 可變長度參數(shù)

            · 靜態(tài)引入

            · 元數(shù)據(jù)(注解)

            · C風格的格式化輸出

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

            1.4. 泛型

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

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

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

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

            我們可以看到,在這個簡單的例子中,我們在定義aList的時候指明了它是一個直接受Integer類型的ArrayList,當我們調(diào)用aList.get(0)時,我們已經(jīng)不再需要先顯式的將結果轉換成Integer,然后再賦值給myInteger了。而這一步在早先的Java版本中是必須的。也許你在想,在使用Collection時節(jié)約一些類型轉換就是Java泛型的全部嗎?遠不止。單就這個例子而言,泛型至少還有一個更大的好處,那就是使用了泛型的容器類變得更加健壯:早先,Collection接口的get()和Iterator接口的next()方法都只能返回Object類型的結果,我們可以把這個結果強制轉換成任何Object的子類,而不會有任何編譯期的錯誤,但這顯然很可能帶來嚴重的運行期錯誤,因為在代碼中確定從某個Collection中取出的是什么類型的對象完全是調(diào)用者自己說了算,而調(diào)用者也許并不清楚放進Collection的對象具體是什么類的;就算知道放進去的對象“應該”是什么類,也不能保證放到Collection的對象就一定是那個類的實例。現(xiàn)在有了泛型,只要我們定義的時候指明該Collection接受哪種類型的對象,編譯器可以幫我們避免類似的問題溜到產(chǎn)品中。我們在實際工作中其實已經(jīng)看到了太多的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>的子類。真的是這樣嗎?我們仔細想一想就會發(fā)現(xiàn)這樣做可能會帶來的問題:如果我們可以把ArrayList<Integer>向上轉型為ArrayList<Object>,那么在往這個轉了型以后的ArrayList中添加對象的時候,我們豈不是可以添加任何類型的對象(因為Object是所有對象的公共父類)?這顯然讓我們的ArrayList<Integer>失去了原本的目的。于是Java編譯器禁止我們這樣做。那既然是這樣,ArrayList<Integer>以及ArrayList<String>、ArrayList<Double>等等有沒有公共的父類呢?有,那就是ArrayList<?>。?在這里叫做通配符。我們?yōu)榱丝s小通配符所指代的范圍,通常也需要這樣寫: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具體是使用哪個類作為參數(shù)定義的,因此編譯器也無法讓這段代碼編譯通過。舉例來講,如果我們想往這個ArrayList中放一個SomeExtendedClass2的對象,我們?nèi)绾伪WC它實際上不是其他的如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泛型的最大特點在于它是在語言級別實現(xiàn)的,區(qū)別于C# 2.0中的CLR級別。這樣的做法使得JRE可以不必做大的調(diào)整,缺點是無法支持一些運行時的類型甄別。一旦編譯,它就被寫死了,能提供的動態(tài)能力相當弱。

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

            1.5. 增強的for循環(huán)

            你是否已經(jīng)厭倦了每次寫for循環(huán)時都要寫上那些機械的代碼,尤其當你需要遍歷數(shù)組或者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語句的三個表達式順序弄錯;第二個表達式邏輯判斷不正確(漏掉一些、多出一些、甚至死循環(huán));忘記移動游標;在循環(huán)體內(nèi)不小心改變了游標的位置等等。為什么不能讓編譯器幫我們處理這些細節(jié)呢?在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);
              }
              }

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

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

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

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

            1.6. 自動裝箱/自動拆箱

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

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

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

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

            1.7. 類型安全的枚舉

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

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

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

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

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

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

              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中的枚舉沒有太多本質(zhì)上的區(qū)別,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");
              }

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

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

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

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

              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);
              }
              在這個枚舉中,我們定義了四個元素,分別對應加減乘除四則運算,對于每一種運算,我們都可以調(diào)用eval()方法,而具體的方法實現(xiàn)各異。我們可以通過下面的代碼來試驗上面這個枚舉類:
              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));
              }
              }

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

            1.8. 可變長度參數(shù)

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

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

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

            1.9. 靜態(tài)引入

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

            import static java.lang.Math.PI;

            或者

            import static java.lang.Math.*;

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

            1.10. 元數(shù)據(jù)(注解)

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

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

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

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

            我們來看現(xiàn)成的有哪些有用的注解:

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

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

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

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

              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支持三種形式的注解:不帶參數(shù)的標記注解、帶一個參數(shù)的注解和帶多個參數(shù)的完整注解。下面分別舉例說明:

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

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

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

            注解的最大作用在于它在源代碼的基礎上增加了有用的信息,使得源代碼的描述性更強。這些信息可以被代碼之外的工具識別,從而可以很方便的增加外部功能,以及減少不必要的相關代碼/文件的維護。這里我想簡單提一個超出J2SE(TM) 5.0范疇的話題:在未來的EJB 3.0規(guī)范中會有相當多的對注解的應用,讓我們預覽一下將來的無狀態(tài)會話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(),這一特性依賴于前邊提到的可變長度參數(shù)。舉個例子來說,我們現(xiàn)在可以寫:

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

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

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

          常用鏈接

          留言簿(3)

          隨筆分類(22)

          隨筆檔案(76)

          文章分類(12)

          文章檔案(17)

          搜索

          •  

          積分與排名

          • 積分 - 250887
          • 排名 - 227

          最新評論

          評論排行榜


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

          主站蜘蛛池模板: 获嘉县| 开封县| 澜沧| 泰兴市| 北宁市| 肃宁县| 德江县| 潞城市| 邵阳市| 大名县| 宁远县| 东兴市| 蓬溪县| 靖州| 乐陵市| 恩施市| 永德县| 吐鲁番市| 康保县| 瑞丽市| 丰顺县| 仙游县| 绥中县| 西安市| 邛崃市| 五大连池市| 天峨县| 和林格尔县| 库伦旗| 陆良县| 定兴县| 深圳市| 东丽区| 正阳县| 永登县| 容城县| 锡林郭勒盟| 武城县| 政和县| 临朐县| 南昌市|