(轉貼) jqGrid整理

          原帖地址:
          http://www.cnblogs.com/mycoding/archive/2011/07/07/2099878.html

          一、 jqGrid的加載。

          1.引用相關頭文件

          引入CSS:

          <link href="Scripts/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />

          <link href="Scripts/ui.jqgrid.css" rel="stylesheet" type="text/css" />

          引入JS:

          <script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>

          <script src="Scripts/jquery-ui.min.js" type="text/javascript"></script>

          <script src="Scripts/grid.locale-en.js" type="text/javascript"></script>

          <script src="Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>

          因為jqGrid3.6及以后的版本集成了jQuery UI,所以,此處需要導入UI相關js和css。另外grid.locale-en.js這個語言文件必須在jquery.jqGrid.min.js之前加載,否則會出問題。

          2.將jqgrid加入頁面中

          根據jqGrid的文檔,要想生成一個jqGrid,最直接的方法就是:

          $("#list").jqGrid(options);

          其中list是頁面上的一個table:<table id="list"></table>

          下面是一個簡單的例子:

          <script type="text/javascript">
           
          $(document).ready(function () {
           
          jQuery("#list").jqGrid({
           
          url: 'Handler.ashx',
           
          datatype: "json",
           
          mtype: 'GET',
           
          colNames: ['SalesReasonID', 'Name', 'ReasonType', 'ModifiedDate'],
           
          colModel: [
           
          { name: 'SalesReasonID', index: 'SalesReasonID', width: 40, align: "left", editable: true },
           
          { name: 'Name', index: 'Name', width: 100, align: "center" },
           
          { name: 'ReasonType', index: 'ReasonType', width: 100, align: "center" },
           
          { name: 'ModifiedDate', index: 'ModifiedDate', width: 150, align: "center", search: false }
           
          ],
           
          rowList: [10, 20, 30],
           
          sortname: 'SalesReasonID',
           
          viewrecords: true,
           
          sortorder: "desc",
           
          jsonReader: {
           
          root: "griddata",
           
          total: "totalpages",
           
          page: "currpage",
           
          records: "totalrecords",
           
          repeatitems: false
           
          },
           
          pager: jQuery('#pager'),
           
          rowNum: 5,
           
          altclass: 'altRowsColour',
           
          //width: 'auto',
           
          width: '500',
           
          height: 'auto',
           
          caption: "DemoGrid"
           
          }).navGrid('#pager', { add: true, edit: true, del: true,search:false,refresh:false }); ;
           
          })

          二、 jqgrid的重要選項

          具體的options參考,可以訪問jqGrid文檔關于option的章節(http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options)。其中有幾個是比較常用的,重點介紹一下:

          • url :jqGrid控件通過這個參數得到需要顯示的數據,具體的返回值可以使XML也可以是Json。
          • datatype :這個參數用于設定將要得到的數據類型。類型包括:json 、xml、xmlstring、local、javascript、function。
          • mtype : 定義使用哪種方法發起請求,GET或者POST。
          • height :Grid的高度,可以接受數字、%值、auto,默認值為150。
          • width :Grid的寬度,如果未設置,則寬度應為所有列寬的之和;如果設置了寬度,則每列的寬度將會根據shrinkToFit選項的設置,進行設置。
          • shrinkToFit :此選項用于根據width計算每列寬度的算法。默認值為true。如果shrinkToFit為true且設置了width值,則每列寬度會根據 width成比例縮放;如果shrinkToFit為false且設置了width值,則每列的寬度不會成比例縮放,而是保持原有設置,而Grid將會有 水平滾動條。
          • autowidth :默認值為false。如果設為true,則Grid的寬度會根據父容器的寬度自動重算。重算僅發生在Grid初始化的階段;如果當父容器尺寸變化了,同時也需要變化Grid的尺寸的話,則需要在自己的代碼中調用setGridWidth方法來完成。
          • pager :定義頁碼控制條Page Bar,在上面的例子中是用一個div(<div id=”pager”></div>)來放置的。
          • sortname :指定默認的排序列,可以是列名也可以是數字。此參數會在被傳遞到Server端。
          • viewrecords :設置是否在Pager Bar顯示所有記錄的總數。
          • caption :設置Grid表格的標題,如果未設置,則標題區域不顯示。
          • rowNum :用于設置Grid中一次顯示的行數,默認值為20。正是這個選項將參數rows(prmNames中設置的)通過url選項設置的鏈接傳遞到Server。注意如果Server返回的數據行數超過了rowNum的設定,則Grid也只顯示rowNum設定的行數。
          • rowList :一個數組,用于設置Grid可以接受的rowNum值。例如[10,20,30]。
          • colNames :字符串數組,用于指定各列的題頭文本,與列的順序是對應的。
          • colModel :最重要的數組之一,用于設定各列的參數。(稍后詳述)
          • prmNames :這是一個數組,用于設置jqGrid將要向Server傳遞的參數名稱。(稍后詳述)
          • jsonReader :這又是一個數組,用來設定如何解析從Server端發回來的json數據。(稍后詳述)

          2.1 prmNames選項

          prmNames是jqGrid的一個重要選項,用于設置jqGrid將要向Server傳遞的參數名稱。其默認值為:

          prmNames : {

          page:"page", // 表示請求頁碼的參數名稱

          rows:"rows", // 表示請求行數的參數名稱

          sort: "sidx", // 表示用于排序的列名的參數名稱

          order: "sord", // 表示采用的排序方式的參數名稱

          search:"_search", // 表示是否是搜索請求的參數名稱

          nd:"nd", // 表示已經發送請求的次數的參數名稱

          id:"id", // 表示當在編輯數據模塊中發送數據時,使用的id的名稱

          oper:"oper", // operation參數名稱

          editoper:"edit", // 當在edit模式中提交數據時,操作的名稱

          addoper:"add", // 當在add模式中提交數據時,操作的名稱

          deloper:"del", // 當在delete模式中提交數據時,操作的名稱

          subgridid:"id", // 當點擊以載入數據到子表時,傳遞的數據名稱

          npage: null,

          totalrows:"totalrows" // 表示需從Server得到總共多少行數據的參數名稱,參見jqGrid選項中的rowTotal

          }

          2.2 jsonReader選項

          jsonReader是jqGrid的一個重要選項,用于設置如何解析從Server端發回來的json數據,如果Server返回的是xml數據,則對應的使用xmlReader來解析。jsonReader的默認值為:

          jsonReader : {

          root: "rows", // json中代表實際模型數據的入口

          page: "page", // json中代表當前頁碼的數據

          total: "total", // json中代表頁碼總數的數據

          records: "records", // json中代表數據行總數的數據

          repeatitems: true, // 如果設為false,則jqGrid在解析json時,會根據name來搜索對應的數據元素(即可以json中元素可以不按順序);而所使用的name是來自于colModel中的name設定。

          cell: "cell",

          id: "id",

          userdata: "userdata",

          subgrid: {

          root:"rows",

          repeatitems: true,

          cell:"cell"

          }

          }

          假如有下面一個json字符串:

          {"totalpages":"3","currpage":"1","totalrecords":"11","griddata": [{"SalesReasonID":"1","Name":"Price","ReasonType":"Other","ModifiedDate":"1998 年6月1日"},{"SalesReasonID":"2","Name":"On Promotion","ReasonType":"Promotion","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"3","Name":"Magazine Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"4","Name":"Television Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"5","Name":"Manufacturer","ReasonType":"Other","ModifiedDate":"1998 年6月1日"}]}

          其對應的jsonReader為:jsonReader: {

          root: "griddata",

          total: "totalpages",

          page: "currpage",

          records: "totalrecords",

          repeatitems: false

          }

          注:cell、id在repeatitems為true時可以用到,即每一個記錄是由一對id和cell組合而成,即可以適用另一種json結構。援引文檔中的例子:

          repeatitems為true時:

          jQuery("#gridid").jqGrid({  

               ...  

               jsonReader : {  

                   root:"invdata",  

                   page: "currpage",  

                   total: "totalpages",  

                   records: "totalrecords"

               },  

               ...  

          });  

          json結構為:

          {   

          "totalpages": "xxx",   

          "currpage": "yyy",  

          "totalrecords": "zzz",  

          "invdata" : [  

                            {"id" :"1", "cell" :["cell11", "cell12", "cell13"]},   // cell中不需要各列的name,只要值就OK了,但是需要保持對應

                            {"id" :"2", "cell" :["cell21", "cell22", "cell23"]},  

                            ...  

               ]  

          }  

          repeatitems為false時:

          jQuery("#gridid").jqGrid({  

               ...  

               jsonReader : {  

                   root:"invdata",  

                   page: "currpage",  

                   total: "totalpages",  

                   records: "totalrecords",  

                   repeatitems: false,  

                   id: "0"

               },  

               ...  

          });  

          json結構為:

          {   

          "totalpages" : "xxx",   

          "currpage" : "yyy",  

          "totalrecords" : "zzz",  

          "invdata" : [  

                           {"invid" : "1","invdate":"cell11", "amount" :"cell12", "tax" :"cell13", "total" :"1234", "note" :"somenote"}, // 數據中需要各列的name,但是可以不按列的順序

                            {"invid" : "2","invdate":"cell21", "amount" :"cell22", "tax" :"cell23", "total" :"2345", "note" :"some note"},  

                            ...  

               ]  

          }  

          2.3 colModel的重要選項

          colModel也有許多非常重要的選項,在使用搜索、排序等方面都會用到。這里先只說說最基本的。

          • name :為Grid中的每個列設置唯一的名稱,這是一個必需選項,其中保留字包括subgrid、cb、rn。
          • index :設置排序時所使用的索引名稱,這個index名稱會作為sidx參數(prmNames中設置的)傳遞到Server。
          • label :當jqGrid的colNames選項數組為空時,為各列指定題頭。如果colNames和此項都為空時,則name選項值會成為題頭。
          • width :設置列的寬度,目前只能接受以px為單位的數值,默認為150。
          • sortable :設置該列是否可以排序,默認為true。
          • search :設置該列是否可以被列為搜索條件,默認為true。
          • resizable :設置列是否可以變更尺寸,默認為true。
          • hidden :設置此列初始化時是否為隱藏狀態,默認為false。
          • formatter :預設類型或用來格式化該列的自定義函數名。常用預設格式有:integer、date、currency、number等(具體參見文檔 )。

          三、 注意事項

          1. 動態改變Add Form或者Edit Form中的select的內容,如:改變下圖中的Comparator下拉中的內容。

          clip_image002

          $("#list_d").navGrid('#pager_d',{add:true,edit:true,del:true,search:false,refresh:false},

          {

          checkOnSubmit:false, closeAfterEdit: true,recreateForm:true,

          beforeInitData:function(formid){

          initComparator();

          },

          beforeShowForm: function(formid){

          $("#list_d").jqGrid('setColProp', 'Name', { editrules:{required:false},});

          $('#tr_Name', formid).hide();

          }

          },//edit

          {},//add

          {}//del

          beforeInitData, beforeShowForm在每次點擊編輯的時候都會執行。initComparator的作用是通過ajax獲取數據,然后利 用$("#list_d").jqGrid('setColProp', 'Comparator', { editoptions: { value: valueString} });來設置Comparator下拉中的內容。其中valueString的格式如下’ equal to: equal to; not equal to: not equal to’。鍵值之間用冒號隔開,2項之間用分號隔開。注意:把recreateForm設為true,否則'setColProp'只在第一次調用時有效。

          2. var rowNum = parseInt($(this).getGridParam("records"), 10); 得到數據條數。

          3. jQuery("#list_d").clearGridData();清空數據。

          4. jQuery("#list").getCell(ids,"Key");獲取第ids行的key列。

          5. $("#list").jqGrid('setSelection', "1");選中第一行。放在loadComplete:中在gird加載完成的時候自動選中第一行。 loadComplete:function(data){$("#list").jqGrid('setSelection', "1");

          }

          6. 對于像1中的可編輯的字段,可以設定rule,參見http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules#editrules

          7. 修改Option,以URL為例

          jQuery("#list_d").jqGrid('setGridParam',{url:"xxx.aspx",page:1}).trigger('reloadGrid');


          復雜的表格可以參考jquery grid demo網站 :




          posted @ 2011-11-01 14:23 AK47 閱讀(2323) | 評論 (0)編輯 收藏

          (轉載)Spring 注解@Component,@Service,@Controller,@Repository

          Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應用程序采用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋,而用 @Component 對那些比較中立的類進行注釋。

          在 一個稍大的項目中,通常會有上百個組件,如果這些組件采用xml的bean定義來配置,顯然會增加配置文件的體積,查找以及維護起來也不太方便。 Spring2.5為我們引入了組件自動掃描機制,他可以在類路徑底下尋找標注了 @Component,@Service,@Controller,@Repository注解的類,并把這些類納入進spring容器中管理。它的作用 和在xml文件中使用bean節點配置組件時一樣的。要使用自動掃描機制,我們需要打開以下配置信息: 
          Java代碼

          1. <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-2.5.xsd"  
          2. >  
          3.   
          4. <context:component-scan base-package=”com.eric.spring”>   
          5. </beans>   
             /*其中base-package為需要掃描的包(含所有子包)

               @Service用于標注業務層組件,

               @Controller用于標注控制層組件(如struts中的action),

               @Repository用于標注數據訪問組件,即DAO組件,

               @Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。

              */   


          6. @Service public class VentorServiceImpl implements iVentorService {   
          7. } @Repository public class VentorDaoImpl implements iVentorDao {  
          8. }

          /*getBean的默認名稱是類名(頭字母小 寫),如果想自定義,可以@Service(“aaaaa”)這樣來指定,這種bean默認是單例的,如果想改變,可以使用 @Service(“beanName”) @Scope(“prototype”)來改變。可以使用以下方式指定初始化方法和銷毀方法(方法名任意): @PostConstruct public void init() {  

          */
          9. }  
          10. @PreDestroy public void destory() {  
          11. } 

          注入方式:

          把 DAO實現類注入到service實現類中,把service的接口(注意不要是service的實現類)注入到action中,注入時不要new 這個注入的類,因為spring會自動注入,如果手動再new的話會出現錯誤,然后屬性加上@Autowired后不需要getter()和 setter()方法,Spring也會自動注入。至于更具體的內容,等對注入的方式更加熟練后會做個完整的例子上來。

          注解:

          在 spring的配置文件里面只需要加上<context:annotation-config/> 和<context:component-scan base-package="需要實現注入的類所在包"/>,可以使用base-package="*"表示全部的類。   

          <context:component-scan base-package=”com.eric.spring”> 

          其中base-package為需要掃描的包(含所有子包)

          在接口前面標上@Autowired和@Qualifier注釋使得接口可以被容器注入,當接口存在兩個實現類的時候必須指定其中一個來注入,使用實現類首字母小寫的字符串來注入,如:

          1.     @Autowired     
          2.   
          3.     @Qualifier("chinese")      
          4.   
          5.     private Man man;   

          否則可以省略,只寫@Autowired   。 

          @Service服務層組件,用于標注業務層組件,表示定義一個bean,自動根據bean的類名實例化一個首寫字母為小寫的bean,例如Chinese實例化為chinese,如果需要自己改名字則:@Service("你自己改的bean名")。   

          @Controller用于標注控制層組件(如struts中的action)

          @Repository持久層組件,用于標注數據訪問組件,即DAO組件

          @Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。 


          @Service 
          public class VentorServiceImpl implements iVentorService { 
          }

          @Repository 
          public class VentorDaoImpl implements iVentorDao { 


          getBean 的默認名稱是類名(頭字母小寫),如果想自定義,可以@Service(“aaaaa”) 這樣來指定,這種

          bean默認是單例的,如果想改變,可以使用@Service(“beanName”) @Scope(“prototype”)來改變。

          可以使用以下方式指定初始化方法和銷毀方法(方法名任意):

          @PostConstruct

          public void init() { 



          @PreDestroy

          public void destory() { 

          }

          posted @ 2011-10-10 16:46 AK47 閱讀(49719) | 評論 (3)編輯 收藏

          (轉貼)使用 Spring 2.5 注釋驅動的 IoC 功能

          原帖地址
          http://www.ibm.com/developerworks/cn/java/j-lo-spring25-ioc/

          概述

          注釋配置相對于 XML 配置具有很多的優勢:

          • 它可以充分利用 Java 的反射機制獲取類結構信息,這些信息可以有效減少配置的工作。如使用 JPA 注釋配置 ORM 映射時,我們就不需要指定 PO 的屬性名、類型等信息,如果關系表字段和 PO 屬性名、類型都一致,您甚至無需編寫任務屬性映射信息——因為這些信息都可以通過 Java 反射機制獲取。
          • 注釋和 Java 代碼位于一個文件中,而 XML 配置采用獨立的配置文件,大多數配置信息在程序開發完成后都不會調整,如果配置信息和 Java 代碼放在一起,有助于增強程序的內聚性。而采用獨立的 XML 配置文件,程序員在編寫一個功能時,往往需要在程序文件和配置文件中不停切換,這種思維上的不連貫會降低開發效率。

          因此在很多情況下,注釋配置比 XML 配置更受歡迎,注釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多注釋類,現在您已經可以使用注釋配置完成大部分 XML 配置的功能。在這篇文章里,我們將向您講述使用注釋進行 Bean 定義和依賴注入的內容。

           
          原來我們是怎么做的      
          在使用注釋配置之前,先來回顧一下傳統上是如何配置 Bean 并完成 Bean 之間依賴關系的建立。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類需要在 Spring 容器中配置為 Bean:    
             
          Office 僅有一個屬性:    
               
          清單 1. Office.java    
                              
          package com.baobaotao;    
          public class Office {    
              private String officeNo =”001”;    
             
              //省略 get/setter    
             
              @Override   
              public String toString() {    
                  return "officeNo:" + officeNo;    
              }    
          }    
                 
          Car 擁有兩個屬性:    
               
          清單 2. Car.java 
                               
          package com.baobaotao;    
             
          public class Car {    
              private String brand;    
              private double price;    
             
              // 省略 get/setter    
             
              @Override   
              public String toString() {    
                  return "brand:" + brand + "," + "price:" + price;    
              }    
          }    
                
          Boss 擁有 Office 和 Car 類型的兩個屬性:    
            
          清單 3. Boss.java    
                              
          package com.baobaotao;    
             
          public class Boss {    
              private Car car;    
              private Office office;    
             
              // 省略 get/setter    
             
              @Override   
              public String toString() {    
                  return "car:" + car + "\n" + "office:" + office;    
              }    
          }    
              
          我們在 Spring 容器中將 Office 和 Car 聲明為 Bean,并注入到 Boss Bean 中:下面是使用傳統 XML 完成這個工作的配置文件 beans.xml:    
              
          清單 4. beans.xml 將以上三個類配置成 Bean    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
              xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    
              <bean id="boss" class="com.baobaotao.Boss">    
                  <property name="car" ref="car"/>    
                  <property name="office" ref="office" />    
              </bean>    
              <bean id="office" class="com.baobaotao.Office">    
                  <property name="officeNo" value="002"/>    
              </bean>    
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
               
          當我們運行以下代碼時,控制臺將正確打出 boss 的信息:    
            
          清單 5. 測試類:AnnoIoCTest.java    
                              
          import org.springframework.context.ApplicationContext;    
          import org.springframework.context.support.ClassPathXmlApplicationContext;    
          public class AnnoIoCTest {    
             
              public static void main(String[] args) {    
                  String[] locations = {"beans.xml"};    
                  ApplicationContext ctx =     
                      new ClassPathXmlApplicationContext(locations);    
                  Boss boss = (Boss) ctx.getBean("boss");    
                  System.out.println(boss);    
              }    
          }    
              
          這說明 Spring 容器已經正確完成了 Bean 創建和裝配的工作。    
               
          使用 @Autowired 注釋    
             
          Spring 2.5 引入了 @Autowired 注釋,它可以對類成員變量、方法及構造函數進行標注,完成自動裝配的工作。來看一下使用 @Autowired 進行成員變量自動注入的代碼:    
            
          清單 6. 使用 @Autowired 注釋的 Boss.java    
                              
          package com.baobaotao;    
          import org.springframework.beans.factory.annotation.Autowired;    
             
          public class Boss {    
             
              @Autowired   
              private Car car;    
             
              @Autowired   
              private Office office;    
             
              …    
          }    
                 
          Spring 通過一個 BeanPostProcessor 對 @Autowired 進行解析,所以要讓 @Autowired 起作用必須事先在 Spring 容器中聲明 AutowiredAnnotationBeanPostProcessor Bean。   

          清單 7. 讓 @Autowired 注釋工作起來    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
              xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    
             
              <!-- 該 BeanPostProcessor 將自動起作用,對標注 @Autowired 的 Bean 進行自動注入 -->    
              <bean class="AutowiredAnnotationBeanPostProcessor  
                  org.springframework.beans.factory.annotation.  "/>    
             
              <!-- 移除 boss Bean 的屬性注入配置的信息 -->    
              <bean id="boss" class="com.baobaotao.Boss"/>    
               
              <bean id="office" class="com.baobaotao.Office">    
                  <property name="officeNo" value="001"/>    
              </bean>    
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
               
              
          這 樣,當 Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有 @Autowired 注釋時就找到和其匹配(默認按類型匹配)的 Bean,并注入到對應的地方中去。    
             
          按 照上面的配置,Spring 將直接采用 Java 反射機制對 Boss 中的 car 和 office 這兩個私有成員變量進行自動注入。所以對成員變量使用 @Autowired 后,您大可將它們的 setter 方法(setCar() 和 setOffice())從 Boss 中刪除。    
             
          當然,您也可以通過 @Autowired 對方法或構造函數進行標注,來看下面的代碼:    
              
          清單 8. 將 @Autowired 注釋標注在 Setter 方法上    
                              
          package com.baobaotao;    
             
          public class Boss {    
              private Car car;    
              private Office office;    
             
               @Autowired   
              public void setCar(Car car) {    
                  this.car = car;    
              }    
               
              @Autowired   
              public void setOffice(Office office) {    
                  this.office = office;    
              }    
              …    
          }    
               
          這時,@Autowired 將查找被標注的方法的入參類型的 Bean,并調用方法自動注入這些 Bean。而下面的使用方法則對構造函數進行標注:    
              
          清單 9. 將 @Autowired 注釋標注在構造函數上    
                              
          package com.baobaotao;    
             
          public class Boss {    
              private Car car;    
              private Office office;    
               
              @Autowired   
              public Boss(Car car ,Office office){    
                  this.car = car;    
                  this.office = office ;    
              }    
               
              …    
          }    
                 
          由于 Boss() 構造函數有兩個入參,分別是 car 和 office,@Autowired 將分別尋找和它們類型匹配的 Bean,將它們作為 Boss(Car car ,Office office) 的入參來創建 Boss Bean。    
               
          當候選 Bean 數目不為 1 時的應對方法    
             
          在 默認情況下使用 @Autowired 注釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個。當找不到一個匹配的 Bean 時,Spring 容器將拋出 BeanCreationException 異常,并指出必須至少擁有一個匹配的 Bean。我們可以來做一個實驗:    
             
             
          清單 10. 候選 Bean 數目為 0 時    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
               xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">    
               
              <bean class="AutowiredAnnotationBeanPostProcessor  
                  org.springframework.beans.factory.annotation.  "/>     
             
              <bean id="boss" class="com.baobaotao.Boss"/>    
             
              <!-- 將 office Bean 注釋掉 -->    
              <!-- <bean id="office" class="com.baobaotao.Office">    
              <property name="officeNo" value="001"/>    
              </bean>-->    
             
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
               
          由于 office Bean 被注釋掉了,所以 Spring 容器中將沒有類型為 Office 的 Bean 了,而 Boss 的 office 屬性標注了 @Autowired,當啟動 Spring 容器時,異常就產生了。    
             
          當 不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false),這等于告訴 Spring:在找不到匹配 Bean 時也不報錯。來看一下具體的例子:    
             
             
          清單 11. 使用 @Autowired(required = false)    
                              
          package com.baobaotao;    
             
          import org.springframework.beans.factory.annotation.Autowired;    
          import org.springframework.beans.factory.annotation.Required;    
             
          public class Boss {    
             
              private Car car;    
              private Office office;    
             
              @Autowired   
              public void setCar(Car car) {    
                  this.car = car;    
              }    
              @Autowired(required = false)    
              public void setOffice(Office office) {    
                  this.office = office;    
              }    
              …    
          }    
              
          當 然,一般情況下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自動注入而又允許不注入的情況一般僅會在開發期或測試期碰到(如為了快速啟動 Spring 容器,僅引入一些模塊的 Spring 配置文件),所以 @Autowired(required = false) 會很少用到。    
             
          和找不到一個類型匹配 Bean 相反的一個錯誤是:如果 Spring 容器中擁有多個候選 Bean,Spring 容器在啟動時也會拋出 BeanCreationException 異常。來看下面的例子:    
              
          清單 12. 在 beans.xml 中配置兩個 Office 類型的 Bean    
                              
          …     
          <bean id="office" class="com.baobaotao.Office">    
              <property name="officeNo" value="001"/>    
          </bean>    
          <bean id="office2" class="com.baobaotao.Office">    
              <property name="officeNo" value="001"/>    
          </bean>    
          …    
               
          我們在 Spring 容器中配置了兩個類型為 Office 類型的 Bean,當對 Boss 的 office 成員變量進行自動注入時,Spring 容器將無法確定到底要用哪一個 Bean,因此異常發生了。    
             
          Spring 允許我們通過 @Qualifier 注釋指定注入 Bean 的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:    
            
          清單 13. 使用 @Qualifier 注釋指定注入 Bean 的名稱    
                              
          @Autowired   
          public void setOffice(@Qualifier("office")Office office) {    
              this.office = office;    
          }    
              
           
          @Qualifier("office") 中的 office 是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。@Autowired 可以對成員變量、方法以及構造函數進行注釋,而 @Qualifier 的標注對象是成員變量、方法入參、構造函數入參。正是由于注釋對象的不同,所以 Spring 不將 @Autowired 和 @Qualifier 統一成一個注釋類。下面是對成員變量和構造函數入參進行注釋的代碼:    
             
          對成員變量進行注釋:    
            
          清單 14. 對成員變量使用 @Qualifier 注釋    
                              
          public class Boss {    
              @Autowired   
              private Car car;    
               
              @Autowired   
              @Qualifier("office")    
              private Office office;    
              …    
          }    
               
              
          對構造函數入參進行注釋:    
              
          清單 15. 對構造函數變量使用 @Qualifier 注釋    
                              
          public class Boss {    
              private Car car;    
              private Office office;    
             
              @Autowired   
              public Boss(Car car , @Qualifier("office")Office office){    
                  this.car = car;    
                  this.office = office ;    
              }    
          }    
               
          @Qualifier 只能和 @Autowired 結合使用,是對 @Autowired 有益的補充。一般來講,@Qualifier 對方法簽名中入參進行注釋會降低代碼的可讀性,而對成員變量注釋則相對好一些。    
              
             
          使用 JSR-250 的注釋    
             
          Spring 不但支持自己定義的 @Autowired 的注釋,還支持幾個由 JSR-250 規范定義的注釋,它們分別是 @Resource、@PostConstruct 以及 @PreDestroy。    
             
          @Resource   
             
          @Resource 的作用相當于 @Autowired,只不過 @Autowired 按 byType 自動注入,面 @Resource 默認按 byName 自動注入罷了。@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將 @Resource 注釋的 name 屬性解析為 Bean 的名字,而 type 屬性則解析為 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。    
             
          Resource 注釋類位于 Spring 發布包的 lib/j2ee/common-annotations.jar 類包中,因此在使用之前必須將其加入到項目的類庫中。來看一個使用 @Resource 的例子:    
             
          清單 16. 使用 @Resource 注釋的 Boss.java    
                              
          package com.baobaotao;    
             
          import javax.annotation.Resource;    
             
          public class Boss {    
              // 自動注入類型為 Car 的 Bean    
              @Resource   
              private Car car;    
             
              // 自動注入 bean 名稱為 office 的 Bean    
              @Resource(name = "office")    
              private Office office;    
          }    
               
          一般情況下,我們無需使用類似于 @Resource(type=Car.class) 的注釋方式,因為 Bean 的類型信息可以通過 Java 反射從代碼中獲取。    
             
          要讓 JSR-250 的注釋生效,除了在 Bean 類中標注這些注釋外,還需要在 Spring 容器中注冊一個負責處理這些注釋的 BeanPostProcessor:    
             
          <bean     
            class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>    
               
            
          CommonAnnotationBeanPostProcessor 實現了 BeanPostProcessor 接口,它負責掃描使用了 JSR-250 注釋的 Bean,并對它們進行相應的操作。    
             
          @PostConstruct 和 @PreDestroy   
             
          Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執行特定的操作,您既可以通過實現 InitializingBean/DisposableBean 接口來定制初始化之后 / 銷毀之前的操作方法,也可以通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之后 / 銷毀之前調用的操作方法。關于 Spring 的生命周期,筆者在《精通 Spring 2.x—企業應用開發精解》第 3 章進行了詳細的描述,有興趣的讀者可以查閱。    
             
          JSR-250 為初始化之后/銷毀之前方法的指定定義了兩個注釋類,分別是 @PostConstruct 和 @PreDestroy,這兩個注釋只能應用于方法上。標注了 @PostConstruct 注釋的方法將在類實例化后調用,而標注了 @PreDestroy 的方法將在類銷毀之前調用。    
            
          清單 17. 使用 @PostConstruct 和 @PreDestroy 注釋的 Boss.java    
                              
          package com.baobaotao;    
             
          import javax.annotation.Resource;    
          import javax.annotation.PostConstruct;    
          import javax.annotation.PreDestroy;    
             
          public class Boss {    
              @Resource   
              private Car car;    
             
              @Resource(name = "office")    
              private Office office;    
             
              @PostConstruct   
              public void postConstruct1(){    
                  System.out.println("postConstruct1");    
              }    
             
              @PreDestroy   
              public void preDestroy1(){    
                  System.out.println("preDestroy1");     
              }    
              …    
          }    
               
          您只需要在方法前標注 @PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化后或銷毀之前被 Spring 容器執行了。    
             
          我 們知道,不管是通過實現 InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的 init-method/destroy-method 屬性進行配置,都只能為 Bean 指定一個初始化 / 銷毀的方法。但是使用 @PostConstruct 和 @PreDestroy 注釋卻可以指定多個初始化 / 銷毀方法,那些被標注 @PostConstruct 或 @PreDestroy 注釋的方法都會在初始化 / 銷毀時被執行。    
             
          通過以下的測試代碼,您將可以看到 Bean 的初始化 / 銷毀方法是如何被執行的:    
            
          清單 18. 測試類代碼    
                              
          package com.baobaotao;    
             
          import org.springframework.context.support.ClassPathXmlApplicationContext;    
             
          public class AnnoIoCTest {    
             
              public static void main(String[] args) {    
                  String[] locations = {"beans.xml"};    
                  ClassPathXmlApplicationContext ctx =     
                      new ClassPathXmlApplicationContext(locations);    
                  Boss boss = (Boss) ctx.getBean("boss");    
                  System.out.println(boss);    
                  ctx.destroy();// 關閉 Spring 容器,以觸發 Bean 銷毀方法的執行    
              }    
          }    
               
             
          這 時,您將看到標注了 @PostConstruct 的 postConstruct1() 方法將在 Spring 容器啟動時,創建 Boss Bean 的時候被觸發執行,而標注了 @PreDestroy 注釋的 preDestroy1() 方法將在 Spring 容器關閉前銷毀 Boss Bean 的時候被觸發執行。    
                 
          使用 <context:annotation-config/> 簡化配置    
             
          Spring 2.1 添加了一個新的 context 的 Schema 命名空間,該命名空間對注釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道注釋本身是不會做任何事情的,它僅提供元數據信息。要使元數 據信息真正起作用,必須讓負責處理這些元數據的處理器工作起來。     
             
          而我們前面所介紹的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是處理這些注釋元數據的處理器。但是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 為我們提供了一種方便的注冊這些 BeanPostProcessor 的方式,這就是 <context:annotation-config/>。請看下面的配置:    
               
          清單 19. 調整 beans.xml 配置文件    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
               xmlns:context="http://www.springframework.org/schema/context"   
               xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
           http://www.springframework.org/schema/context     
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
               
              <context:annotation-config/>     
             
              <bean id="boss" class="com.baobaotao.Boss"/>    
              <bean id="office" class="com.baobaotao.Office">    
                  <property name="officeNo" value="001"/>    
              </bean>    
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
                
          <context:annotationconfig/> 將隱式地向 Spring 容器注冊 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 這 4 個 BeanPostProcessor。    
             
          在配置文件中使用 context 命名空間之前,必須在 <beans> 元素中聲明 context 命名空間。    
             
              
          使用 @Component   
             
          雖 然我們可以通過 @Autowired 或 @Resource 在 Bean 類中使用自動注入功能,但是 Bean 還是在 XML 文件中通過 <bean> 進行定義 —— 也就是說,在 XML 配置文件中定義 Bean,通過 @Autowired 或 @Resource 為 Bean 的成員變量、方法入參或構造函數入參提供自動注入的功能。能否也通過注釋定義 Bean,從 XML 配置文件中完全移除 Bean 定義的配置呢?答案是肯定的,我們通過 Spring 2.5 提供的 @Component 注釋就可以達到這個目標了。    
             
          下面,我們完全使用注釋定義 Bean 并完成 Bean 之間裝配:    
             
             
          清單 20. 使用 @Component 注釋的 Car.java    
                              
          package com.baobaotao;    
             
          import org.springframework.stereotype.Component;    
             
          @Component   
          public class Car {    
              …    
          }    
               
               
          僅需要在類定義處,使用 @Component 注釋就可以將一個類定義了 Spring 容器中的 Bean。下面的代碼將 Office 定義為一個 Bean:    
              
          清單 21. 使用 @Component 注釋的 Office.java    
                              
          package com.baobaotao;    
             
          import org.springframework.stereotype.Component;    
             
          @Component   
          public class Office {    
              private String officeNo = "001";    
              …    
          }    
               
          這樣,我們就可以在 Boss 類中通過 @Autowired 注入前面定義的 Car 和 Office Bean 了。    
             
          清單 22. 使用 @Component 注釋的 Boss.java    
                              
          package com.baobaotao;    
             
          import org.springframework.beans.factory.annotation.Autowired;    
          import org.springframework.beans.factory.annotation.Required;    
          import org.springframework.beans.factory.annotation.Qualifier;    
          import org.springframework.stereotype.Component;    
             
          @Component("boss")    
          public class Boss {    
              @Autowired   
              private Car car;    
             
              @Autowired   
              private Office office;    
              …    
          }    
              
          @Component 有一個可選的入參,用于指定 Bean 的名稱,在 Boss 中,我們就將 Bean 名稱定義為“boss”。一般情況下,Bean 都是 singleton 的,需要注入 Bean 的地方僅需要通過 byType 策略就可以自動注入了,所以大可不必指定 Bean 的名稱。    
             
          在使用 @Component 注釋后,Spring 容器必須啟用類掃描機制以啟用注釋驅動 Bean 定義和注釋驅動 Bean 自動注入的策略。Spring 2.5 對 context 命名空間進行了擴展,提供了這一功能,請看下面的配置:    
              
          清單 23. 簡化版的 beans.xml    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
              xmlns:context="http://www.springframework.org/schema/context"   
              xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
           http://www.springframework.org/schema/context     
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
              <context:component-scan base-package="com.baobaotao"/>    
          </beans>    
                
          這 里,所有通過 <bean> 元素定義 Bean 的配置內容已經被移除,僅需要添加一行 <context:component-scan/> 配置就解決所有問題了——Spring XML 配置文件得到了極致的簡化(當然配置元數據還是需要的,只不過以注釋形式存在罷了)。<context:component-scan/> 的 base-package 屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。    
             
          <context:component-scan/> 還允許定義過濾器將基包下的某些類納入或排除。Spring 支持以下 4 種類型的過濾方式,通過下表說明:    
             
          表 1. 掃描過濾方式    
          過濾器類型 說明     
          注釋 假如 com.baobaotao.SomeAnnotation 是一個注釋類,我們可以將使用該注釋的類過濾出來。     
          類名指定 通過全限定類名進行過濾,如您可以指定將 com.baobaotao.Boss 納入掃描,而將 com.baobaotao.Car 排除在外。     
          正則表達式 通過正則表達式定義過濾的類,如下所示: com\.baobaotao\.Default.*     
          AspectJ 表達式 通過 AspectJ 表達式定義過濾的類,如下所示: com. baobaotao..*Service+     
             
          下面是一個簡單的例子:    
             
          <context:component-scan base-package="com.baobaotao">    
              <context:include-filter type="regex"     
                  expression="com\.baobaotao\.service\..*"/>    
              <context:exclude-filter type="aspectj"     
                  expression="com.baobaotao.util..*"/>    
          </context:component-scan>    
                
          值 得注意的是 <context:component-scan/> 配置項不但啟用了對類包進行掃描以實施注釋驅動 Bean 定義的功能,同時還啟用了注釋驅動自動注入的功能(即還隱式地在內部注冊了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此當使用 <context:component-scan/> 后,就可以將 <context:annotation-config/> 移除了。    
             
          默認情況下通過 @Component 定義的 Bean 都是 singleton 的,如果需要使用其它作用范圍的 Bean,可以通過 @Scope 注釋來達到目標,如以下代碼所示:    
             
          清單 24. 通過 @Scope 指定 Bean 的作用范圍    
                              
          package com.baobaotao;    
          import org.springframework.context.annotation.Scope;    
          …    
          @Scope("prototype")    
          @Component("boss")    
          public class Boss {    
              …    
          }    
               
          這樣,當從 Spring 容器中獲取 boss Bean 時,每次返回的都是新的實例了。    
                
          采用具有特殊語義的注釋    
             
          Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應用程序采用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋,而用 @Component 對那些比較中立的類進行注釋。    
                  
          注釋配置和 XML 配置的適用場合    
             
          是否有了這些 IOC 注釋,我們就可以完全摒除原來 XML 配置的方式呢?答案是否定的。有以下幾點原因:    
             
          注 釋配置不一定在先天上優于 XML 配置。如果 Bean 的依賴關系是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那么注釋配置優于 XML 配置;反之如果這種依賴關系會在部署時發生調整,XML 配置顯然又優于注釋配置,因為注釋是對 Java 源代碼的調整,您需要重新改寫源代碼并重新編譯才可以實施調整。     
          如果 Bean 不是自己編寫的類(如 JdbcTemplate、SessionFactoryBean 等),注釋配置將無法實施,此時 XML 配置是唯一可用的方式。     
          注釋配置往往是類級別的,而 XML 配置則可以表現得更加靈活。比如相比于 @Transaction 事務注釋,使用 aop/tx 命名空間的事務配置更加靈活和簡單。     
          所 以在實現應用中,我們往往需要同時使用注釋配置和 XML 配置,對于類級別且不會發生變動的配置可以優先考慮注釋配置;而對于那些第三方類以及容易發生調整的配置則應優先考慮使用 XML 配置。Spring 會在具體實施 Bean 創建和 Bean 注入之前將這兩種配置方式的元信息融合在一起。    
                 
          小結    
             
          Spring 在 2.1 以后對注釋配置提供了強力的支持,注釋配置功能成為 Spring 2.5 的最大的亮點之一。合理地使用 Spring 2.5 的注釋配置,可以有效減少配置的工作量,提高程序的內聚性。但是這并不意味著傳統 XML 配置將走向消亡,在第三方類 Bean 的配置,以及那些諸如數據源、緩存池、持久層操作模板類、事務管理等內容的配置上,XML 配置依然擁有不可替代的地位。

          posted @ 2011-10-10 15:49 AK47 閱讀(332) | 評論 (0)編輯 收藏

          (轉貼)數據庫三范式經典實例解析

          數據庫的設計范式是數據庫設計所需要滿足的規范,滿足這些規范的數據庫是簡潔的、結構明晰的,同時,不會發生插入(insert)、刪除(delete)和更新(update)操作異常。反之則是亂七八糟,不僅給數據庫的編程人員制造麻煩,而且面目可憎,可能存儲了大量不需要的冗余信息。
               設計范式是不是很難懂呢?非也,大學教材上給我們一堆數學公式我們當然看不懂,也記不住。所以我們很多人就根本不按照范式來設計數據庫。
               實質上,設計范式用很形象、很簡潔的話語就能說清楚,道明白。本文將對范式進行通俗地說明,并以筆者曾經設計的一個簡單論壇的數據庫為例來講解怎樣將這些范式應用于實際工程。

          范式說明
               第一范式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。
               例如,如下的數據庫表是符合第一范式:

          字段1 字段2 字段3 字段4
          ? ? ? ?
           而這樣的數據庫表是不符合第一范式的:
          字段1 字段2 字段3 字段4
          ? ? 字段3.1 字段3.2 ?

           

               很顯然,在當前的任何關系數據庫管理系統(DBMS)中,傻瓜也不可能做出不符合第一范式的數據庫,因為這些DBMS不允許你把數據庫表的一列再分成二列或多列。因此,你想在現有的DBMS中設計出不符合第一范式的數據庫都是不可能的。
               第二范式(2NF):數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的情況),也即所有非關鍵字段都完全依賴于任意一組候選關鍵字。
               假定選課關系表為SelectCourse(學號, 姓名, 年齡, 課程名稱, 成績, 學分),關鍵字為組合關鍵字(學號, 課程名稱),因為存在如下決定關系:
               (學號, 課程名稱) → (姓名, 年齡, 成績, 學分)
               這個數據庫表不滿足第二范式,因為存在如下決定關系:
               (課程名稱) → (學分)
               (學號) → (姓名, 年齡)
          即存在組合關鍵字中的字段決定非關鍵字的情況。
               由于不符合2NF,這個選課關系表會存在如下問題:
               (1) 數據冗余:
               同一門課程由n個學生選修,"學分"就重復n-1次;同一個學生選修了m門課程,姓名和年齡就重復了m-1次。
               (2) 更新異常:
               若調整了某門課程的學分,數據表中所有行的"學分"值都要更新,否則會出現同一門課程學分不同的情況。
               (3) 插入異常:
               假設要開設一門新的課程,暫時還沒有人選修。這樣,由于還沒有"學號"關鍵字,課程名稱和學分也無法記錄入數據庫。
               (4) 刪除異常:
               假設一批學生已經完成課程的選修,這些選修記錄就應該從數據庫表中刪除。但是,與此同時,課程名稱和學分信息也被刪除了。很顯然,這也會導致插入異常。

               把選課關系表SelectCourse改為如下三個表:
               學生:Student(學號, 姓名, 年齡);
               課程:Course(課程名稱, 學分);
               選課關系:SelectCourse(學號, 課程名稱, 成績)。
               這樣的數據庫表是符合第二范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
               另外,所有單關鍵字的數據庫表都符合第二范式,因為不可能存在組合關鍵字。
               第三范式(3NF):在第二范式的基礎上,數據表中如果不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。所謂傳遞函數依賴,指的是如果存在"A → B → C"的決定關系,則C傳遞函數依賴于A。因此,滿足第三范式的數據庫表應該不存在如下依賴關系:
               關鍵字段 → 非關鍵字段x → 非關鍵字段y
               假定學生關系表為Student(學號, 姓名, 年齡, 所在學院, 學院地點, 學院電話),關鍵字為單一關鍵字"學號",因為存在如下決定關系:
               (學號) → (姓名, 年齡, 所在學院, 學院地點, 學院電話)
          這個數據庫是符合2NF的,但是不符合3NF,因為存在如下決定關系:
               (學號) → (所在學院) → (學院地點, 學院電話)
          即存在非關鍵字段"學院地點"、"學院電話"對關鍵字段"學號"的傳遞函數依賴。
               它也會存在數據冗余、更新異常、插入異常和刪除異常的情況,讀者可自行分析得知。
               把學生關系表分為如下兩個表:
               學生:(學號, 姓名, 年齡, 所在學院);
               學院:(學院, 地點, 電話)。
          這樣的數據庫表是符合第三范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
               鮑依斯-科得范式(BCNF):在第三范式的基礎上,數據庫表中如果不存在任何字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。
               假設倉庫管理關系表為StorehouseManage(倉庫ID, 存儲物品ID, 管理員ID, 數量),且有一個管理員只在一個倉庫工作;一個倉庫可以存儲多種物品。這個數據庫表中存在如下決定關系:
               (倉庫ID, 存儲物品ID) →(管理員ID, 數量)
               (管理員ID, 存儲物品ID) → (倉庫ID, 數量)
               所以,(倉庫ID, 存儲物品ID)和(管理員ID, 存儲物品ID)都是StorehouseManage的候選關鍵字,表中的唯一非關鍵字段為數量,它是符合第三范式的。但是,由于存在如下決定關系:
               (倉庫ID) → (管理員ID)
               (管理員ID) → (倉庫ID)
          即存在關鍵字段決定關鍵字段的情況,所以其不符合 BCNF范式。它會出現如下異常情況:
               (1) 刪除異常:
               當倉庫被清空后,所有"存儲物品ID"和"數量"信息被刪除的同時,"倉庫ID"和"管理員ID"信息也被刪除了。
               (2) 插入異常:
               當倉庫沒有存儲任何物品時,無法給倉庫分配管理員。
               (3) 更新異常:
               如果倉庫換了管理員,則表中所有行的管理員ID都要修改。
               把倉庫管理關系表分解為二個關系表:
               倉庫管理:StorehouseManage(倉庫ID, 管理員ID);
               倉庫:Storehouse(倉庫ID, 存儲物品ID, 數量)。
               這樣的數據庫表是符合BCNF范式的,消除了刪除異常、插入異常和更新異常。


          原帖地址: http://www.cublog.cn/u/23975/showart.php?id=391210

          posted @ 2011-02-21 14:45 AK47 閱讀(325) | 評論 (0)編輯 收藏

          Hibernate 實體對象的生命周期匯總

          本帖匯總了網上幾篇關于hibernate的生命周期的帖子。

          轉載:

          實體對象的生命周期在Hibernate應用中是一個很關鍵的概念,正確的理解實體對象的生命周期將對我們應用Hibernate做持久層設計起到很大的作用.而所謂的實體對象的生命周期就是指實體對象由產生到被GC回收的一段過程.在這過程中我們需要理解的就是實體對象生命周期中的三種狀態.

          1. 自由狀態(Transient)
          所謂的Transient狀態,即實體對象在內存中自由存在,與數據庫中的記錄無關,通常是我們的J2EE中 VO,并沒有被納入Hibernate的實體管理容器.

          1    Test test = new Test();
          2        test.setName("energykk");
          3        //此時的test對象處于Transient(自由狀態)并沒有被Hibernate框架所管理
          4        

          2.持久狀態(Persistent)
          何謂 Persistent? 即實體對象已經處于被Hibernate實體管理容器容器所管理的狀態.這種狀態下這個實體對象的引用將被納入Hibernate實體管理容器容器所管理.
          處于Persistent狀態的實體對象,對它的變更也將被固化到數據庫中.
          在J2EE中通常指的是一個PO.
          Transaction tr = session.beginTransaction();
                  session.save(test);
                  
          //此時的test對象已經處于Persistent(持久狀態)它被Hibernate 納入實體管理容器
                  tr.commit();
                  Transaction tr2 
          = session.beginTransaction();
                  test.setName(
          "xukai");
                  
          //在這個事務中我們并沒有顯示的調用save()方法但是由于Persistent狀態的對象將會自動的固化到
                  
          //數據庫中,因此此時正處在Persistent狀態的test對象的變化也將自動被同步到數據庫中
                  tr2.commit();

          處于Persistent狀態的實體可以簡單的理解為:如果一個實體對象與session發生了關聯,并且處于session的有效期內,那么這個實體對象就處于Persistent狀態.

          3.游離狀態(Detached)
          處于Persistent狀態的實體對象,其對應的session關閉以后,那么這個實體就處于 Detached狀態.
          我們可以認為session對象就是一個Persistent的宿主,一旦這個宿主失效,那么這個實體就處于 Detached狀態.

          session.close();
                  
          //與test對象關聯的session被關閉,因此此時的test對象進入 Detached(游離狀態)
                  
                  session2 
          = HibernateSessionFactory.getSession();
                  Transaction tr3 
          = session2.beginTransaction();
                  session2.update(test);
                  
          //此時正處于Detached狀態的test對象由于再次借助與session2被納入到Hibernate的實體管理容器所以此時的
                  
          //test對象恢復到Persistent狀態
                  test.setName("jjjj");
                  tr3.commit();
                  
                  session2.close();

          既然Transient狀態的實體與Detached狀態的實體都與Hibernate的實體管理容器沒有關系,那他們到底存在哪些差異?
          差異就在于處于Transient狀態的只有一個Name的屬性.此時的test對象所包含的數據信息僅限于此,他與數據庫中的記錄沒有任何瓜葛.
          但是處于Detached狀態的實體已經不止包含Name這個屬性,還被賦予了主鍵也就是通常POJO里的id屬性,由于id是主鍵,他可以確定數據庫表中的一條
          唯一的記錄,那么自然的處于Detached狀態的實體就能與數據庫表中擁有相同id的記錄相關聯.
          這就是他們之間所存在的差異, 簡而言之,Transient狀態的實體缺乏與數據庫表記錄之間的聯系,而Detached狀態的試題恰恰相反.只不過是脫離了session這個數據庫操作平臺而已.
          原帖地址 : http://www.aygfsteel.com/energykk/archive/2007/05/08/115927.html

           生命周期圖:
          原圖地址:http://hi.baidu.com/quest2run/blog/item/39e1d08c7dbd45f4503d9222.html
           

          persistence context



          生命周期特征總結 :
          原帖地址 : http://blog.csdn.net/hgd250/archive/2008/08/06/2775943.aspx
          Transient:

              與數據庫中的記錄沒有任何關系,即沒有與其相關聯的數據庫記錄.
              與session沒有任何關系.即沒有通過session對象的實例對其進行任何持久化的操作
          Persistent:
              每個persistent狀態的實體對象都與一個session對象的實例相關聯
              處于 Persistent狀態的實體對象是與數據庫中的記錄相關聯的.
              Hibernate會依據persistent狀態的實體對象的屬性變化而改變數據庫中相對應的記錄
          .
          Detached:
              游離態是由持久態實體對象轉變而來的.
              游離態實體不再與session對象相關聯.
              游離態實體對象與數據庫中的記錄沒有直接聯系,對其所做的任何修改將不會影響到到數據庫中的數據.
              游離態實體對象在數據庫有相對應的數據記錄,如果沒有被其他事務刪除.

          posted @ 2011-02-14 14:26 AK47 閱讀(334) | 評論 (0)編輯 收藏

          (轉貼)BigInteger 和 BigDecimal

          高精度數字
          Java 提供了兩個類專門用于進行高精度運算BigInteger 和 BigDecimal ,盡管它們可大致劃分到與封裝器相同的類別里,但兩者都沒有對應的主類型;這兩個類都有自己的一系列方法,類似于我們針對主類型執行的操作,也就是說能用 int 或float 做的事情,用BigInteger和BigDecimal 一樣可以做,只是必須換用方法調用,而不是使用運算符。此外由于牽涉更多,所以運算速度會慢一點總之我們犧牲了速度,但換來了精度。

          高精度浮點數BigDecimal

          一些非整數值(如幾美元和幾美分這樣的小數)需要很精確。浮點數不是精確值,所以使用它們會導致舍入誤差。因此,使用浮點數來試圖表示象貨幣量這樣的精確數量不是一個好的想法。使用浮點數來進行美元和美分計算會得到災難性的后果。浮點數最好用來表示象測量值這類數值,這類值從一開始就不怎么精確。
              從 JDK 1.3 起,Java 開發人員就有了另一種數值表示法來表示非整數:BigDecimal。BigDecimal 是標準的類,在編譯器中不需要特殊支持,它可以表示任意精度的小數,并對它們進行計算。在內部,可以用任意精度任何范圍的值和一個換算因子來表示 BigDecimal,換算因子表示左移小數點多少位,從而得到所期望范圍內的值。因此,用 BigDecimal 表示的數的形式為 unscaledValue*10-scale。
          用于加、減、乘和除的方法給  BigDecimal 值提供了算術運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產生新的 BigDecimal 對象。因此,因為創建對象的開銷,BigDecimal 不適合于大量的數學計算,但設計它的目的是用來精確地表示小數。如果您正在尋找一種能精確表示如貨幣量這樣的數值,則 BigDecimal 可以很好地勝任該任務。
          如浮點類型一樣,BigDecimal 也有一些令人奇怪的行為。尤其在使用 equals() 方法來檢測數值之間是否相等時要小心。equals() 方法認為,兩個表示同一個數但換算值不同(例如,100.00 和  100.000)的 BigDecimal 值是不相等的。然而,compareTo() 方法會認為這兩個數是相等的,所以在從數值上比較兩個  BigDecimal 值時,應該使用 compareTo() 而不是 equals()。
          另外還有一些情形,任意精度的小數運算仍不能表示精確結果。例如,1 除以 9 會產生無限循環的小數 .111111...。出于這個原因,在進行除法運算時,BigDecimal 可以讓您顯式地控制舍入。movePointLeft() 方法支持 10 的冪次方的精確除法。
          對于 BigDecimal,有幾個可用的構造函數。其中一個構造函數以雙精度浮點數作為輸入,另一個以整數和換算因子作為輸入,還有一個以小數的 String 表示作為輸入。要小心使用  BigDecimal(double) 構造函數, 因為如果不了解它,會在計算過程中產生舍入誤差。請使用基于整數或 String 的構造函數。
          如果使用 BigDecimal(double) 構造函數不恰當,在傳遞給 JDBC setBigDecimal() 方法時,會造成似乎很奇怪的 JDBC 驅動程序中的異常。例如,考慮以下 JDBC 代碼,該代碼希望將數字 0.01 存儲到小數字段:
            PreparedStatement ps =connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
            ps.setString(1, "penny");
            ps.setBigDecimal(2, new BigDecimal(0.01));
            ps.executeUpdate();
               在執行這段似乎無害的代碼時會拋出一些令人迷惑不解的異常(這取決于具體的 JDBC 驅動程序),因為 0.01 的雙精度近似值會導致大的換算值,這可能會使 JDBC 驅動程序或數據庫感到迷惑。JDBC 驅動程序會產生異常,但可能不會說明代碼實際上錯在哪里,除非意識到二進制浮點數的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 構造 BigDecimal 來避免這類問題, 因為這兩種方法都可以精確地表示小數。
           

          code :

          import java.math.BigDecimal;
          /** * *
          * <p>Title: 開源,開放</p>
          * * <p>Description: opeansource</p>
          * * <p>Copyright: Copyright (c) 2004</p>
          * * <p>Company: ?海棠</p>
          * * @author HaiTang Ming
          * * @version 1.0 */
          public class BigDecimalUtil { 
          //默認除法運算精度,及即保留小數點多少位 
          private static final int DEF_DIV_SCALE = 2; 
          //這個類不能實例化 
          private BigDecimalUtil (){   } 
          /**   
            * * 提供精確的加法運算。   
            * * @param v1 被加數   
            * * @param v2 加數   
            * * @return 兩個參數的和   
            * */ 
          public static double add(double v1,double v2){   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.add(b2)).doubleValue(); 

          /**

            *提供精確的減法運算。 
            * * @param v1 被減數 
            * * @param v2 減數 
            * * @return 兩個參數的差
            **/ 
          public static double sub(double v1,double v2){   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.subtract(b2)).doubleValue(); 

          /**   
            * * 提供精確的乘法運算。   
            * * @param v1 被乘數   
            * * @param v2 乘數   
            * * @return 兩個參數的積   
            * */
          public static double mul(double v1,double v2){   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.multiply(b2)).doubleValue(); 

          /**   
            * * 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到   
            * * 小數點以后多少位,以后的數字四舍五入。   
            * * @param v1 被除數   
            * * @param v2 除數   
            * * @return 兩個參數的商   
            * */ 
          public static double div(double v1,double v2){   
            return div(v1,v2,DEF_DIV_SCALE); 

          /**   
            * * 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指   
            * * 定精度,以后的數字四舍五入。   
            * * @param v1 被除數 
            * @param v2 除數   
            * * @param scale 表示表示需要精確到小數點以后幾位。   
            * * @return 兩個參數的商   
            * */ 
          public static double div(double v1,double v2,int scale){   
            if(scale<0){     
             throw new IllegalArgumentException("The scale must be a positive integer or zero");   
            }   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 

          /**   
            * * 提供精確的小數位四舍五入處理。   
            * * @param v 需要四舍五入的數字   
            * * @param scale 小數點后保留幾位   
            * * @return 四舍五入后的結果   
            * */ 
          public static double round(double v,int scale){ 
            if(scale<0){     
             throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }   
            BigDecimal b = new BigDecimal(Double.toString(v));   
            BigDecimal one = new BigDecimal("1");   
            return (b.divide(one,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 
          }   
          public static void main(String[] args){   
            System.out.println(add(234.44,534.90));
           
            double a = 123.345678;   
            double d = round(a,2);   
            System.out.println("round("+a+",2)--->"+d); 
          }
          }

          高精度整數BigInteger

          BigInteger支持任意精度的整數,也就是說我們可精確表示任意大小的整數值;同時在運算過程中不會丟失任何信息;
          在BigInteger類中有所有的基本算術運算方法,如加、減、乘、除,以及可能會用到的位運算如或、異或、非、左移、右移等。下面是一些方法的例子:當然,如果要有更多的使用方法,可以查閱java api 。

          code :public class BigIntegerTest { 
          public BigIntegerTest() {   } 
          /**   
            * * 測試BigInteger
            * */ 
          public static void testBigInteger() {   
            BigInteger bi = new BigInteger("888");   
            //multiply :乘法   
            BigInteger result = bi.multiply(new BigInteger("2"));   
            System.out.println(result);   
            //divide : 除法   
            result = bi.divide(new BigInteger("2"));   
            System.out.println(result);   
            //add : 加法   
            result = bi.add(new BigInteger("232"));   
            System.out.println(result);   
            //subtract :減法   
            result = bi.subtract(new BigInteger("23122"));   
            System.out.println(result);   
            result = bi.shiftRight(2);   
            System.out.println(result); 
          }   
          public static void main(String[] args) {   
            testBigInteger(); 
          }
          }
          原貼地址http://dev.firnow.com/course/3_program/java/javaxl/2008914 /142796_2.html

          posted @ 2010-12-10 14:16 AK47 閱讀(1031) | 評論 (0)編輯 收藏

          (轉貼) 超大整數相加,超過了long的范圍,你要怎么做!

           

          引用:

           這個只能夠用字符串的形式來處理了,因為計算機能夠處理的最大是long型,本文以字符串的形式來進行超大數據的相加,理論上只要你的內存允許,相加多大的數都可以。

          /**

           * 超大整數相加:

           * 題目要求:如果系統要使用超大整數(超過long的范圍),請你設計一個數據結構來存儲這種

           * 超大型數字以及設計一種算法來實現超大整數的加法運算

           * @author Administrator

           *

           */

          public class VeryBigNumAdd {

           

              /**

               * @param args

               */

              public static void main(String[] args) {

                 // TODO Auto-generated method stub

                 /*

                 String a="1223232";

                 for(int i=a.length()-1;i>=0;i--)

                 {

                     System.out.print(a.charAt(i));

                 }

                 */

                 VeryBigNumAdd vbn=new VeryBigNumAdd();

                 String a="123453243455535634535252345234677576252241234123523453664563634";

                 String b="123453243455535634535252345234677576252241234123523453664563634";

                 String result=vbn.doAdd(a,b);

                 System.out.println("result:"+result);

              }

              /**

               *

               * @param a 加數字符串1

               * @param b 加數字符串2

               * @return 結果字符串

               * 分析:

               * 1、取得兩個字符串的長度

               * 2、把兩個的長度做比較,并得出較長的長度,及較短的長度

               * 3、把長度較短的加數字符串,在左面補0,使之與較長的字符串一樣長

               * 4、從最高位,一個個數的取出來相加,當然首先得轉換為整型

               * 5、設置進位,如果兩個數相加及加上進位大于等于10,并且這不是最左邊一個字符相加,相加結果等于

               *    (取出1+取出2+進位)-10,并把進位設為1;如果沒有大于10,就把進位設為0,如些循環,把

               *    相加的結果以字符串的形式結合起來,就得到最后的結果

               */

              String doAdd(String a,String b)

              {

                 String str="";

                 int lenA=a.length();

                 int lenB=b.length();

                 int maxLen=(lenA>lenB) ? lenA : lenB;

                 int minLen=(lenA<lenB) ? lenA : lenB;

                 String strTmp="";

                 for(int i=maxLen-minLen;i>0;i--)

                 {

                     strTmp+="0";

                 }

                 //把長度調整到相同

                 if(maxLen==lenA)

                 {

                     b=strTmp+b;

                 }else

                     a=strTmp+a;

                 int JW=0;//進位

                 for(int i=maxLen-1;i>=0;i--)

                 {        

                     int tempA=Integer.parseInt(String.valueOf(a.charAt(i)));

                     int tempB=Integer.parseInt(String.valueOf(b.charAt(i)));

                     int temp;

                     if(tempA+tempB+JW>=10 && i!=0)

                     {

                        temp=tempA+tempB+JW-10;

                        JW=1;

                     }

                     else

                     {

                        temp=tempA+tempB+JW;

                        JW=0;

                     }        

                     str=String.valueOf(temp)+str;        

                 }

                 return str;

              }

           

          }

           

          原帖地址: http://blog.csdn.net/fenglibing/archive/2007/08/23/1756773.aspx

              
              其實java 本身也提供了api ,java.math.BigInteger;import java.math.BigDecimal; 也可以實現。

          code :

          package com.kangdy.test;

          import java.math.BigInteger;
          import java.math.BigDecimal;

          public class NumberTest {
           public static void main(String args[]){
            BigInteger b1= new BigInteger("2222222222222222222222222");
            BigInteger b2= new BigInteger("8888888888888888888888888");
            BigDecimal b3 = new BigDecimal("66666666666666666666666666");
            BigDecimal b4 = new BigDecimal("9999999999999999999999999999");
            System.out.println(b1.add(b2).toString());
            System.out.println(b3.add(b4).toString());
           }
          }

          這里只是給出簡單的例子。



          posted @ 2010-12-10 14:06 AK47 閱讀(928) | 評論 (0)編輯 收藏

          (轉貼)java回調函數

          原帖地址: http://ayzw001.blog.163.com/blog/static/1134114222009420112538726/

          引用:

                 所謂回調,就是客戶程序C調用服務程序S中的某個方法a,然后S又在某個時候反過來調用C中的某個方法b,對于C來說,這個b便叫做回調函數。

          一般說來,C不會自己調用b,C提供b的目的就是讓S來調用它,而且是C不得不提供。由于S并不知道C提供的b叫甚名誰,所以S會約定b的接口規范(函數原型),然后由C提前通過S的一個函數r告訴S自己將要使用b函數,這個過程稱為回調函數的注冊,r稱為注冊函數。

          下面舉個通俗的例子:

          某天,我打電話向你請教問題,當然是個難題,:),你一時想不出解決方法,我又不能拿著電話在那里傻等,于是我們約定:等你想出辦法后打手機通知我,這樣,我就掛掉電話辦其它事情去了。過了XX分鐘,我的手機響了,你興高采烈的說問題已經搞定,應該如此這般處理。故事到此結束。

          這個例子說明了“異步+回調”的編程模式。其中,你后來打手機告訴我結果便是一個“回調”過程;我的手機號碼必須在以前告訴你,這便是注冊回調函數;我的手機號碼應該有效并且手機能夠接收到你的呼叫,這是回調函數必須符合接口規范。

           

          如果你還不太清楚看看這段描述合和代碼:

          聲明一個接口,另外一個類有方法里面有個參數以是這個接口類型的,而后在另外類中實現這個接口(java中多用的是匿名內部類),而且以這個匿名的類生成的對象為參數傳到上面提到類中,而后實現回調.......這種用法可以參考java里面常用到的數據庫操作所用到的幾個接口.....

          //聲明一個接口
          public interface ICallBack {
              void postExec();
          }

           

          //另外一個類有方法里面有個參數以是這個接口類型的
          public class FooBar {
              private ICallBack callBack;
              public void setCallBack(ICallBack callBack) {
                  this.callBack = callBack;
              }
              public void doSth() {
                  callBack.postExec();
              }
          }
          ---------------------------------------
          回調的實現
          public class Test {
              public static void main(String[] args) {
                  FooBar foo = new FooBar();
                  foo.setCallBack(new ICallBack() {
                      public void postExec() {
                          System.out.println("method executed.");
                      }
                  });
                  foo.doSth();//調用函數
              }
          }

          posted @ 2010-12-10 11:22 AK47 閱讀(629) | 評論 (0)編輯 收藏

          (轉貼) 真正理解面向接口編程

          面向對象設計里有一點大家已基本形成共識,就是面向接口編程,我想大多數人對這個是沒有什么覺得需要懷疑的。

          問題是在實際的項目開發中我們是怎么體現的呢? 難道就是每一個實現都提供一個接口就了事了?反過來說,你有時候有沒有覺得接口是多余的事? 又或者,你僅僅是覺得現在類似spring這樣的框架已習慣用接口這種方式而心存當然。

          設計模式解析里提到了面向對象設計考慮的幾個視角,一個是概念層,一個是規約層,一個是實現層。我如果沒有猜錯的話,實際上我們大多數人的眼睛一直是盯著實現層的,而這正是面向對象設計所極力避免的,即你不要在一開始就關注這些細節,你要關注的是規約(接口).

          對于實際項目開發來說,如果我們把實現的過程分為多個階段的話我們不妨這么劃分,第一階段,根據client端的需要去設計我們的規約(interface),在這個階段任何實現都沒有,所有的任務就是定義接口所需要的職責,以及所需要的一些po,vo;第二階段,實現前面定義的規約。而以前我是怎么做的呢? 我是交叉作的,即假模假樣的定義一個接口(其實我心里在想這個東西有屁用),然后定義了一個方法,然后就立即去實現這個方法,再然后我又定義一個方法,繼續去實現,我現在終于想通了,這樣好累,效率很低,最重要的是,這不屬于真正的設計。
          現在我是怎么做的呢?比如一個list.jsp里需要查詢,列表,然后看明細信息,然后增加信息,我會第一步在接口里定義完(這個過程會有整體設計的意識),毫不關心底層實現(數據庫、事務),我的目標就是"我想要這個功能,我想要那個功能",至于那個功能怎么實現在第一階段我認為那不是我的事情(盡管這個事情最終還是由我來做) .大家看這個過程和前面的過程有什么本質的不同呢? 就是分層的概念更加明顯,你的工作更有層次,每次都有先設計再實現的步驟,而前面那個過程很容易就讓你不知不覺地陷入純實現的陷阱中。

          一點感想,歡迎大家拍磚。

          原帖地址: http://www.aygfsteel.com/alex/archive/2007/03/12/103185.html

          posted @ 2010-11-05 14:09 AK47 閱讀(319) | 評論 (0)編輯 收藏

          數字金額的中文大小寫轉化

          曾經去過一家公司面試。筆試題的最后一題是一個數字金額大小寫轉化的問題。
          當時沒想那么多,僅僅想到應該把數拆開然后添加單位的一個大致的設計思路。
          而那個面試官一個勁兒的問我用啥算法。那個題最后也沒答上,回來后比較郁悶,
          在網上搜了一下。這個答案還真不少。不過覺得有一種設計還比較靠譜。
          大概是這樣:
           * 先將整數與小數部分分開,計算小數部分,角分并保存
           * 整數部分長度不足12位,前面加0補足。
           * 將整數部分分割3部分。高4位代表億,中間的是萬,其余分別代表千,百,十,個
           * 定一個方法拼出每一部分串。
           * 最后整數與小數部分合成。

          自己實現了一下,以下是代碼。
          code :

          package com.kangdy.test;
          /**
           * 數字金額轉化成大寫
           * 先將整數與小數部分分開,計算小數部分,角分并保存
           * 整數部分長度不足12位,前面加0補足。
           * 將整數部分分割3部分。高4位代表億,中間的是萬,其余分別代表千,百,十,個
           * 定一個方法拼出每一部分串。
           * 最后整數與小數部分合成。
           * @author dkang
           *
           */
          public class NumberToString {

           String numberStr;

           public static final String unit[] = { "", "十", "百", "千", "萬", "億" };

           public static final String unit2[] = { "元", "角", "分" };

           public static final String numStr[] = { "零", "壹", "貳", "叁", "肆", "伍", "陸",
             "柒", "捌", "玖" };

           /**
            * 字符串長度不足12位用0補足
            *
            * @param str
            * @return
            */
           private String additionalZero(String str) {
            StringBuffer strb = new StringBuffer();
            if (str.length() < 12) {
             int size = 12 - str.length();
             for (int i = 0; i < size; i++) {
              strb.append("0");
             }
            }
            return strb.append(str).toString();
           }

           /**
            * 遞歸拆分數字成字符串
            *
            * @param value
            * @param strBuffer
            * @return
            */
           private String decomposeNumberToString(int value, StringBuffer strBuffer) {
            int quotient = 0;
            quotient = value / 10;
            if (quotient != 0) {
             decomposeNumberToString(quotient, strBuffer);
            }
            int remaider = value % 10;
            strBuffer.append(remaider + ",");
            return strBuffer.toString().substring(0,
              strBuffer.toString().length() - 1);
           }

           /**
            * 使用循環拆分數字成字符串
            *
            * @param value
            * @return
            */
           private String decomposeNumberToString2(int value) {
            StringBuilder strBuilder = new StringBuilder();
            int quotient = value;
            int remaider = 0;
            while (quotient != 0) {
             remaider = quotient % 10;
             strBuilder.append(remaider + ",");
             quotient = quotient / 10;
            }
            strBuilder.deleteCharAt(strBuilder.lastIndexOf(","));
            return strBuilder.reverse().toString();
           }

           /**
            * 添加單位
            *
            * @param temp
            * @return
            */
           private String addUnits(String temp) {
            StringBuffer sb = new StringBuffer();
            String str[] = temp.split(",");
            String tempStr = temp.replace(",", "");
            if (tempStr.contains("000")) {
             return sb.append(resplaceNumToStr(str[0]) + unit[3]).toString();
            } else if (tempStr.contains("00")) {
             if (tempStr.charAt(3) == '0') {
              return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
                resplaceNumToStr(str[1]) + unit[2]).toString();
             } else {
              return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
                numStr[0]).append(resplaceNumToStr(str[3])).toString();
             }
            } else {
             for (int i = 0; i < str.length; i++) {
              sb.append(resplaceNumToStr(str[i]));
              if (!str[i].equals("0")) {
               sb.append(unit[str.length - (i + 1)]);
              }
             }
            }
            return sb.toString();
           }

           /**
            * 數字替換
            *
            * @param str
            * @return
            */
           private String resplaceNumToStr(String str) {
            try {
             int num = Integer.parseInt(str);
             return numStr[num];
            } catch (Exception e) {
             e.printStackTrace();
            }
            return "";
           }

           /**
            * 把4位長度的數字轉化成字符串
            *
            * @param number
            * @param i
            * @return
            */
           private String transformNumberToString(String number, int i) {
            StringBuffer strBuffer = new StringBuffer();
            StringBuilder strBuilder = new StringBuilder();
            try {
             int num = Integer.parseInt(number);
             if (num != 0) {
              String s1 = decomposeNumberToString(num, strBuffer);
              strBuilder.append(addUnits(s1));
              if (i == 1) {
               strBuilder.append(unit[5]);
              } else if (i == 2)
               strBuilder.append(unit[4]);
             }
            } catch (Exception e) {
             e.printStackTrace();
            }
            return strBuilder.toString();
           }

           /**
            * 得到最終結果
            *
            * @param str
            * @return
            */
           public String IntegrationResultString(String str) {
            StringBuffer strBuffer = new StringBuffer();
            String numStr[] = null;
            if (str.indexOf(".") != -1) {
             numStr = str.split("\\.");
            } else {
             return strBuffer.append(createIntegerPartsResult(str)).toString();
            }
            String fractionalStr = createFractionalPartsResult(numStr[1]);
            String integerStr = createIntegerPartsResult(numStr[0]);
            return strBuffer.append(integerStr).append(fractionalStr).toString();
           }

           private String createIntegerPartsResult(String integer) {
            StringBuffer strBuffer = new StringBuffer();
            String temp = additionalZero(integer);
            String str1 = temp.substring(0, 4);
            strBuffer.append(transformNumberToString(str1, 1));
            String str2 = temp.substring(4, 8);
            strBuffer.append(transformNumberToString(str2, 2));
            String str3 = temp.substring(8, temp.length());
            strBuffer.append(transformNumberToString(str3, 3) + unit2[0]);
            return strBuffer.toString();
           }

           private String createFractionalPartsResult(String fractionalStr) {
            StringBuilder strB = new StringBuilder();
            String s1 = fractionalStr.substring(0, 1);
            String s2 = fractionalStr.substring(1, fractionalStr.length());
            if (!s1.equals("0")) {
             strB.append(resplaceNumToStr(s1) + unit2[1]);
            }
            if (!s2.equals("0")) {
             strB.append(resplaceNumToStr(s2) + unit2[2]);
            }
            return strB.toString();
           }

           public static void main(String args[]) {
            NumberToString test = new NumberToString();
            String str = "200123004054.11";
            System.out.println(test.IntegrationResultString(str));
           }
          }



          posted @ 2010-11-02 14:59 AK47 閱讀(617) | 評論 (0)編輯 收藏

          僅列出標題
          共4頁: 上一頁 1 2 3 4 下一頁 
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 大厂| 山阴县| 台州市| 鄂托克旗| 澄迈县| 高青县| 礼泉县| 洛阳市| 荥经县| 洞头县| 宜黄县| 永寿县| 鞍山市| 洛阳市| 虞城县| 岳池县| 石棉县| 大埔县| 长宁县| 会理县| 随州市| 红安县| 五华县| 如皋市| 樟树市| 精河县| 玛多县| 衡阳市| 孝义市| 合水县| 崇左市| 获嘉县| 连南| 思茅市| 临桂县| 临夏市| 乐亭县| 盐源县| 平利县| 屏南县| 永兴县|