posts - 35,  comments - 7,  trackbacks - 0

          隨著Hibernate在Java開發中的廣泛應用,我們在使用Hibernate進行對象持久化操作中也遇到了各種各樣的問題。這些問題往往都是我們對Hibernate缺乏了解所致,這里我講個我從前遇到的問題及一些想法,希望能給大家一點借鑒。
          ?
          ???????這是在一次事務提交時遇到的異常。
          ???????an?assertion?failure?occured?(this?may?indicate?a?bug?in?Hibernate,?but?is?more?likely?due?to?unsafe?use?of?the?session)
          net.sf.hibernate.AssertionFailure:?possible?nonthreadsafe?access?to?session
          注:非possible?non-threadsafe?access?to?the?session?(那是另外的錯誤,類似但不一樣)
          ?
          ???????這個異常應該很多的朋友都遇到過,原因可能各不相同。但所有的異常都應該是在flush或者事務提交的過程中發生的。這一般由我們在事務開始至事務提交的過程中進行了不正確的操作導致,也會在多線程同時操作一個Session時發生,這里我們僅僅討論單線程的情況,多線程除了線程同步外基本與此相同。
          ?
          ???????至于具體是什么樣的錯誤操作那?我給大家看一個例子(假設Hibernate配置正確,為保持代碼簡潔,不引入包及處理任何異常)
          ??

          SessionFactory?sf?=?new?Configuration().configure().buildSessionFactory()?;
          Session?s?=?sf.openSession();
          Cat?cat?=?new?Cat();

          Transaction?tran?=?s.beginTransaction();?(1)
          s.save(cat);?(2)(此處同樣可以為update?delete)
          s.evict(cat);?(3)
          tran.commit();?(4)
          s.close();(5)


          ???????這就是引起此異常的典型錯誤。我當時就遇到了這個異常,檢查代碼時根本沒感覺到這段代碼出了問題,想當然的認為在Session上開始一個事務,通過Session將對象存入數據庫,再將這個對象從Session上拆離,提交事務,這是一個很正常的流程。如果這里正常的話,那問題一定在別處。
          ?
          ????????問題恰恰就在這里,我的想法也許沒有錯,但是一個錯誤的論據所引出的觀點永遠都不可能是正確的。因為我一直以為直接在對數據庫進行操作,忘記了在我與數據庫之間隔了一個Hibernate,Hibernate在為我們提供持久化服務的同時,也改變了我們對數據庫的操作方式,這種方式與我們直接的數據庫操作有著很多的不同,正因為我們對這種方式沒有一個大致的了解造成了我們的應用并未得到預先設想的結果。
          ?
          那Hibernate的持久化機制到底有什么不同那?簡單的說,Hibernate在數據庫層之上實現了一個緩存區,當應用save或者update一個對象時,Hibernate并未將這個對象實際的寫入數據庫中,而僅僅是在緩存中根據應用的行為做了登記,在真正需要將緩存中的數據flush入數據庫時才執行先前登記的所有行為。
          ?
          在實際執行的過程中,每個Session是通過幾個映射和集合來維護所有與該Session建立了關聯的對象以及應用對這些對象所進行的操作的,與我們這次討論有關的有entityEntries(與Session相關聯的對象的映射)、insertions(所有的插入操作集合)、deletions(刪除操作集合)、updates(更新操作集合)。下面我就開始解釋在最開始的例子中,Hibernate到底是怎樣運作的。
          (1)生成一個事務的對象,并標記當前的Session處于事務狀態(注:此時并未啟動數據庫級事務)。
          (2)應用使用s.save保存cat對象,這個時候Session將cat這個對象放入entityEntries,用來標記cat已經和當前的會話建立了關聯,由于應用對cat做了保存的操作,Session還要在insertions中登記應用的這個插入行為(行為包括:對象引用、對象id、Session、持久化處理類)。
          (3)s.evict(cat)將cat對象從s會話中拆離,這時s會從entityEntries中將cat這個對象移出。
          (4)事務提交,需要將所有緩存flush入數據庫,Session啟動一個事務,并按照insert,update,……,delete的順序提交所有之前登記的操作(注意:所有insert執行完畢后才會執行update,這里的特殊處理也可能會將你的程序搞得一團糟,如需要控制操作的執行順序,要善于使用flush),現在cat不在entityEntries中,但在執行insert的行為時只需要訪問insertions就足夠了,所以此時不會有任何的異常。異常出現在插入后通知Session該對象已經插入完畢這個步驟上,這個步驟中需要將entityEntries中cat的existsInDatabase標志置為true,由于cat并不存在于entityEntries中,此時Hibernate就認為insertions和entityEntries可能因為線程安全的問題產生了不同步(也不知道Hibernate的開發者是否考慮到例子中的處理方式,如果沒有的話,這也許算是一個bug吧),于是一個net.sf.hibernate.AssertionFailure就被拋出,程序終止。
          ?
          我想現在大家應該明白例子中的程序到底哪里有問題了吧,我們的錯誤的認為s.save會立即的執行,而將cat對象過早的與Session拆離,造成了Session的insertions和entityEntries中內容的不同步。所以我們在做此類操作時一定要清楚Hibernate什么時候會將數據flush入數據庫,在未flush之前不要將已進行操作的對象從Session上拆離。
          ?
          對于這個錯誤的解決方法是,我們可以在(2)和(3)之間插入一個s.flush()強制Session將緩存中的數據flush入數據庫(此時Hibernate會提前啟動事務,將(2)中的save登記的insert語句登記在數據庫事務中,并將所有操作集合清空),這樣在(4)事務提交時insertions集合就已經是空的了,即使我們拆離了cat也不會有任何的異常了。
          前面簡單的介紹了一下Hibernate的flush機制和對我們程序可能帶來的影響以及相應的解決方法,Hibernate的緩存機制還會在其他的方面給我們的程序帶來一些意想不到的影響。看下面的例子:
          ??

          (name為cat表的主鍵)
          Cat?cat?=?new?Cat();
          cat.setName(“tom”);
          s.save(cat);

          cat.setName(“mary”);
          s.update(cat);(6)

          Cat?littleCat?=?new?Cat();
          littleCat.setName(“tom”);
          s.save(littleCat);

          s.flush();


          這個例子看起來有什么問題?估計不了解Hibernate緩存機制的人多半會說沒有問題,但它也一樣不能按照我們的思路正常運行,在flush過程中會產生主鍵沖突,可能你想問:“在save(littleCat)之前不是已經更改cat.name并已經更新了么?為什么還會發生主鍵沖突那?”這里的原因就是我在解釋第一個例子時所提到的緩存flush順序的問題,Hibernate按照insert,update,……,delete的順序提交所有登記的操作,所以你的s.update(cat)雖然在程序中出現在s.save(littleCat)之前,但是在flush的過程中,所有的save都將在update之前執行,這就造成了主鍵沖突的發生。
          ?
          這個例子中的更改方法一樣是在(6)之后加入s.flush()強制Session在保存littleCat之前更新cat的name。這樣在第二次flush時就只會執行s.save(littleCat)這次登記的動作,這樣就不會出現主鍵沖突的狀況。
          ?
          再看一個例子(很奇怪的例子,但是能夠說明問題)

          Cat?cat?=?new?Cat();
          cat.setName(“tom”);
          s.save(cat);?(7)
          s.delete(cat);(8)

          cat.id=null;(9)
          s.save(cat);(10)
          s.flush();

          ?
          這個例子在運行時會產生異常net.sf.hibernate.HibernateException:?identifier?of?an?instance?of?Cat?altered?from?8b818e920a86f038010a86f03a9d0001?to?null
          ?
          這里例子也是有關于緩存的問題,但是原因稍有不同:
          (7)和(2)的處理相同。
          (8)Session會在deletions中登記這個刪除動作,同時更新entityEntries中該對象的登記狀態為DELETED。
          (9)Cat類的標識符字段為id,將其置為null便于重新分配id并保存進數據庫。
          (10)此時Session會首先在entityEntries查找cat對象是否曾經與Session做過關聯,因為cat只改變了屬性值,引用并未改變,所以會取得狀態為DELETED的那個登記對象。由于第二次保存的對象已經在當前Session中刪除,save會強制Session將緩存flush才會繼續,flush的過程中首先要執行最開始的save動作,在這個save中檢查了cat這個對象的id是否與原來執行動作時的id相同。不幸的是,此時cat的id被賦為null,異常被拋出,程序終止(此處要注意,我們在以后的開發過程盡量不要在flush之前改變已經進行了操作的對象的id)。
          ?
          這個例子中的錯誤也是由于緩存的延時更新造成的(當然,與不正規的使用Hibernate也有關系),處理方法有兩種:
          1、在(8)之后flush,這樣就可以保證(10)處save將cat作為一個全新的對象進行保存。
          2、刪除(9),這樣第二次save所引起的強制flush可以正常的執行,在數據庫中插入cat對象后將其刪除,然后繼續第二次save重新插入cat對象,此時cat的id仍與從前一致。
          ?
          這兩種方法可以根據不同的需要來使用,呵呵,總覺得好像是很不正規的方式來解決問題,但是問題本身也不夠正規,只希望能夠在應用開發中給大家一些幫助,不對的地方也希望各位給與指正。
          ?
            總的來說,由于Hibernate的flush處理機制,我們在一些復雜的對象更新和保存的過程中就要考慮數據庫操作順序的改變以及延時flush是否對程序的結果有影響。如果確實存在著影響,那就可以在需要保持這種操作順序的位置加入flush強制Hibernate將緩存中記錄的操作flush入數據庫,這樣看起來也許不太美觀,但很有效。
          posted @ 2006-05-24 13:47 java小記 閱讀(327) | 評論 (0)編輯 收藏

          ???? public ? static ? void ?main(String[]?args)? throws ?Exception? {?
          ????????System.out.println(isWrapClass(Long.
          class ));?
          ????????System.out.println(isWrapClass(String.
          class ));?
          ????}
          ?

          ????
          public ? static ? boolean ?isWrapClass(Class?clz)? {?
          ????????
          try ? {?
          ????????????
          return ?((Class)?clz.getField( " TYPE " ).get( null )).isPrimitive();?
          ????????}
          ? catch ?(Exception?e)? {?
          ????????????
          return ? false ;?
          ????????}
          ?
          ????}
          ?
          posted @ 2006-05-22 16:44 java小記 閱讀(894) | 評論 (0)編輯 收藏
          <script language="javascript">
          ? g_blnCheckUnload = true;
          ? function RunOnBeforeUnload() {
          ? ?? if (g_blnCheckUnload) {window.event.returnValue = 'You will lose any unsaved content';??
          ??? ?}?
          ? }
          </script>
          <body? onbeforeunload="RunOnBeforeUnload()">
          </body>
          posted @ 2006-05-15 09:27 java小記 閱讀(262) | 評論 (0)編輯 收藏

          ?

          import ?java.io.FileOutputStream;
          import ?java.util.List;

          import ?org.jdom.Attribute;
          import ?org.jdom.Document;
          import ?org.jdom.Element;
          import ?org.jdom.input.SAXBuilder;
          import ?org.jdom.output.Format;
          import ?org.jdom.output.XMLOutputter;

          public ? class ?XML? {
          ????
          public ? static ? void ?main(String[]?args)? throws ?Exception? {

          ????????SAXBuilder?builder?
          = ? new ?SAXBuilder();
          ????????Document?doc?
          = ?builder.build( " d:\\destination.xml " );
          ????????Element?root?
          = ?doc.getRootElement();
          ????????root.removeChild(
          " Target " );
          ????????Element?items?
          = ?root.getChild( " Items " );
          ????????List?item?
          = ?items.getChildren( " item " );
          ????????
          for ?( int ?i? = ? 0 ;?i? < ?item.size();?i ++ )? {
          ????????????Element?elem?
          = ?(Element)?item.get(i);
          ????????????Attribute?attr?
          = ?elem.getAttribute( " displayName " );????????????
          ????????????attr.setValue(attr.getValue()?
          + ? " 中國 " );????????????
          ????????}

          ????????
          // ?保存
          ????????Format?format? = ?Format.getCompactFormat();
          ????????format.setEncoding(
          " gb2312 " );
          ????????format.setIndent(
          " ?? " );
          ????????XMLOutputter?XMLOut?
          = ? new ?XMLOutputter(format);
          ????????XMLOut.output(doc,?
          new ?FileOutputStream( " d:\\destination_tmp.xml " ));

          ????}

          }

          posted @ 2006-05-13 09:22 java小記 閱讀(225) | 評論 (0)編輯 收藏
          STUN (Simple Traversal of UDP through NATs (Network Address Translation)) is a protocol for assisting devices behind a NAT firewall or router with their packet routing.
          • STUN enables a device to find out its public IP address and the type of NAT service its sitting behind.
          • STUN operates on TCP and UDP port 3478.
          • STUN is not widely supported by VOIP devices yet.
          • STUN may use DNS SRV records to find STUN servers attached to a domain. The service name is _stun._udp or _stun._tcp

          Definitions (from the RFC)

          • STUN Client: A STUN client (also just referred to as a client) is an entity that generates STUN requests. A STUN client can execute on an end system, such as a user's PC, or can run in a network element, such as a conferencing server.
          • STUN Server: A STUN Server (also just referred to as a server) is an entity that receives STUN requests, and sends STUN responses. STUN servers are generally attached to the public Internet.

          Various types of NAT (still according to the RFC)
          • Full Cone: A full cone NAT is one where all requests from the same internal IP address and port are mapped to the same external IP address and port. Furthermore, any external host can send a packet to the internal host, by sending a packet to the mapped external address.
          • Restricted Cone: A restricted cone NAT is one where all requests from the same internal IP address and port are mapped to the same external IP address and port. Unlike a full cone NAT, an external host (with IP address X) can send a packet to the internal host only if the internal host had previously sent a packet to IP address X.
          • Port Restricted Cone: A port restricted cone NAT is like a restricted cone NAT, but the restriction includes port numbers. Specifically, an external host can send a packet, with source IP address X and source port P, to the internal host only if the internal host had previously sent a packet to IP address X and port P.
          • Symmetric: A symmetric NAT is one where all requests from the same internal IP address and port, to a specific destination IP address and port, are mapped to the same external IP address and port. If the same host sends a packet with the same source address and port, but to a different destination, a different mapping is used. Furthermore, only the external host that receives a packet can send a UDP packet back to the internal host.
          posted @ 2006-05-12 16:19 java小記 閱讀(322) | 評論 (0)編輯 收藏
          1、密碼由6-32位字母、數字或下劃線構成;
          2、至少需要一位小寫字母;
          3、至少需要一位大寫字母;
          4、至少需要一位數字。

          String?password?=?"password";
          ????????System.out.println(password?
          !=?null?&&?password.length()?>=?6
          ????????????????
          &&?password.length()?<=?32
          ????????????????
          &&?Pattern.compile("[a-z]+").matcher(password).find()
          ????????????????
          &&?Pattern.compile("[A-Z]+").matcher(password).find()
          ????????????????
          &&?Pattern.compile("[\\d]+").matcher(password).find());
          posted @ 2006-05-10 09:10 java小記 閱讀(409) | 評論 (0)編輯 收藏

          ??
          JList組件有一個單獨的顯示模式ListModel來表示JList的顯示數據.??
          JList創建以后,JList數據元素的值及數據元素的數量可以動態地改變.??
          JList在它的數據模式ListModel中觀察數據的改變.因此,一個ListModel 的正確實現應當在每次數據發生改變時,通知事件的監聽者.??
          當使用構造函數JList(Object[])創建一個JList的實例時,系統將自動 創建一個DefaultListModel的實例來存儲JList的顯示數據, 可以調用 DefaultListModel中定義的簡便方法來動態地修改JList的數據,如 removeElementAt(index),addElement(Object)等. DefaultListModel 在修改數據的同時,將通知JList關于數據的改變.??

          ?

          posted @ 2006-04-28 08:28 java小記 閱讀(475) | 評論 (0)編輯 收藏

          ?

          import ?java.awt. * ;
          import ?java.awt.event. * ;
          public ? class ?MyFrame? extends ?Frame {
          {
          public ?MyFrame() {
          ??setSize(
          500 , 400 );
          ??setResizable(?
          false ?);?
          ??
          this .addWindowStateListener( new ?WindowStateListener() { // 狀態監聽器
          ???? public ? void ?windowStateChanged(WindowEvent?e) {
          ??????
          if (getState() == 1 ) { // 最小化狀態
          ????????setState( 0 ); // 切換成正常狀態
          ??????}

          ????}

          ??}
          );
          }


          public ? static ? void ?main(String[]?args) {
          ????
          new ?MyFrame().setVisible( true );
          }


          }
          posted @ 2006-04-28 08:28 java小記 閱讀(2710) | 評論 (0)編輯 收藏
          簡而言之:
          Big endian machine: It thinks the first byte it reads is the biggest.
          Little endian machine: It thinks the first byte it reads is the littlest.
          舉個例子,從內存地址0x0000開始有以下數據
          ?0x0000?????0x12
          ?0x0001?????0x34
          ?0x0002?????0xab
          ?0x0003?????0xcd
          如果我們去讀取一個地址為0x0000的四個字節變量,若字節序為big-endian,則讀出
          結果為0x1234abcd;若字節序位little-endian,則讀出結果為0xcdab3412.
          如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果為
          ????????????????big-endian?????little-endian
          0x0000?????0x12??????????????0xcd
          0x0001?????0x23??????????????0xab
          0x0002?????0xab??????????????0x34
          0x0003?????0xcd??????????????0x12
          x86系列CPU都是little-endian的字節序.
          posted @ 2006-04-25 15:21 java小記 閱讀(329) | 評論 (0)編輯 收藏
          ?VoIP applications like Skype, GoogleTalk and Others

          Internet Voice, also known as Voice over Internet Protocol (VoIP), is a technology that allows you to make telephone calls using a broadband Internet connection instead of a regular (or analog) phone line. Some services using VoIP may only allow you to call other people using the same service, but others may allow you to call anyone who has a telephone number - including local, long distance, mobile, and international numbers. Also, while some services only work over your computer or a special VoIP phone, other services allow you to use a traditional phone through an adaptor.
          posted @ 2006-04-05 09:59 java小記 閱讀(268) | 評論 (1)編輯 收藏
          import ?java.security.MessageDigest;
          import ?java.security.NoSuchAlgorithmException;
          public ? class ?PasswordManager? {
          ????
          ????
          // ***************************************************************
          ????
          // ?Public?Interface
          ????
          // ***************************************************************

          ????
          /**
          ?????*?Creates?a?one-way?has?of?the?specified?password.??This?allows?passwords?to?be
          ?????*?safely?stored?in?the?database?without?any?way?to?retrieve?the?original?value.
          ?????*?
          ?????*?
          @param ?password?the?string?to?encrypt.
          ?????*?
          ?????*?
          @return ?the?encrypted?password,?or?null?if?encryption?failed.
          ?????
          */

          ????
          public ? static ?String?encryptPassword(?String?password?)? {
          ????????
          ????????
          try ? {
          ????????????MessageDigest?md?
          = ?MessageDigest.getInstance( " SHA " );
          ????????
          ????????????
          // Create?the?encrypted?Byte[]
          ????????????md.update(?password.getBytes()?);
          ????????????
          byte []?hash? = ?md.digest();
          ????????????
          ????????????
          // Convert?the?byte?array?into?a?String
          ????????????
          ????????????StringBuffer?hashStringBuf?
          = ? new ?StringBuffer();
          ????????????String?byteString;
          ????????????
          int ?byteLength;
          ????????????
          ????????????
          for (? int ?index? = ? 0 ;?index? < ?hash.length;?index ++ ?)? {
          ????????????????
          ????????????????byteString?
          = ?String.valueOf(?hash[index?]? + ? 128 ?);
          ????????????????
          ????????????????
          // Pad?string?to?3.??Otherwise?hash?may?not?be?unique.
          ????????????????byteLength? = ?byteString.length();
          ????????????????
          switch (?byteLength?)? {
          ????????????????
          case ? 1 :
          ????????????????????byteString?
          = ? " 00 " ? + ?byteString;
          ????????????????????
          break ;
          ????????????????
          case ? 2 :
          ????????????????????byteString?
          = ? " 0 " ? + ?byteString;
          ????????????????????
          break ;
          ????????????????}

          ????????????????hashStringBuf.append(?byteString?);
          ????????????}

          ????????????
          ????????????
          return ?hashStringBuf.toString();
          ????????}

          ????????
          catch (?NoSuchAlgorithmException?nsae?)? {
          ????????????System.out.println(?
          " Error?getting?password?hash?-? " ? + ?nsae.getMessage()?);
          ????????????
          return ? null ;
          ????????}

          ????}

          }
          posted @ 2006-03-28 15:45 java小記 閱讀(275) | 評論 (0)編輯 收藏
          偶爾eclipse3.1的web項目有時候不能自動編譯/WEB-INF/src,即使選擇工程的自動編譯開關也不好使,不知道是不是Bug
          我這樣解決的:
          1. project->properties->java build path->source->.../WEB-INF/src的output folder不要默認,編輯讓它指向../WEB-INF/classes
          然后重新點擊build工程即可自動編譯。

          2. 再就是最重要的要看工程下面是否缺少了work目錄,由于CVS控制時不把work加如版本,所以checkout后沒有這個目錄,要手工加上有的工程就能自動編譯了
          posted @ 2006-03-21 10:50 java小記 閱讀(1320) | 評論 (0)編輯 收藏
          XP系統一般情況下在裝完系統后會有一個計算機管理員權限的用戶,以后登陸時就顯示這個用戶
          進入系統后在控制面板中的用戶帳號下看不到Administrator用戶,就好象丟失了一樣,如何看到呢?
          里面有另外一個問題涉及XP自動登陸                                      
          單擊"開始/運行",輸入"rundll32 netplwiz.dll,UsersRunDll",按回車鍵后彈出“用戶帳戶”窗口,
          這和“控制面板”中打開的“用戶賬戶”窗口不同!
          然后取消選定"要使用本機,用戶必須輸入用戶名和密碼"選項,單擊確定.
          在彈出的對話框中輸入你想讓電腦每次自動登錄的賬戶(默認Administrator)和密碼即可。
          下一次開機自動用Administrator登陸到系統,再看控制面板就有了Administrator。
          Xp默認把Administrator隱藏,雖然都是計算機管理員Administrator和有計算機管理員權限的用戶還是有
          細微差別的。但是一般情況下使用系統用計算機管理員權限的用戶就足夠了
          posted @ 2006-02-13 21:40 java小記 閱讀(5288) | 評論 (3)編輯 收藏


          安全域是Tomcat的內置功能,主要有以下幾種安全域:
          JDBCRealm
          DataSourceRealm
          JNDIRealm
          MemoryRealm
          JAASRealm

          在conf/server.xml中配置應用的<Context......>下面的<Realm className="org.apache.catalina.realm.MemoryRealm" />
          從一個XML文件中讀取用戶信息,默認的就是tomcat-users.xml

          tomcat-users.xml中的角色定義
          <?xml version='1.0' encoding='utf-8'?>
          <tomcat-users> 
              <role rolename="tomcat"/> 
              <role rolename="role1"/> 
              <role rolename="guest"/>
              <user username="lee" password="lee" roles="guest"/>
              <user username="suoxin" password="suoxin" roles="tomcat,role1"/>
          </tomcat-users>

          在應用下的web.xml中加入<security-constraint>元素
          <security-constraint>  
              <web-resource-collection>
                   <url-pattern>/*</url-pattern>   
              </web-resource-collection>   
              <auth-constraint>    
                   <role-name>admin</role-name>   
                   <role-name>guest</role-name>  
              </auth-constraint>
          </security-constraint>

          在應用下的web.xml中加入<login-config>元素
          <login-config>     
             <auth-method>FORM</auth-method>
             <!--這里FORM是基于表單的驗證,會跳轉到mylogin.jsp,如果出錯就到myerror.jsp,
             還有基于對話筐的是BASIC關鍵字,但是不安全在網絡傳輸中。摘要驗證DIGEST會采
             用MD5(Message Digest Algorithm)加密傳輸-->
             <form-login-config>       
                <form-login-page>/mylogin.jsp</form-login-page>       
                <form-error-page>/myerror.jsp</form-error-page>     
             </form-login-config>
          </login-config>

          mylogin.jsp的action和name必須嚴格規范寫
          <form name="form1" id="form1" method="post" action="j_security_check"> 
             <input type="text" name="j_username"/> 
             <input type="text" name="j_password"/>
             <input type="submit" name="Submit" value="提交" />
          </form>

           

          posted @ 2006-02-11 09:11 java小記 閱讀(221) | 評論 (0)編輯 收藏
           
            1,下載OSCache, oscache-2.2-full.zip
            2,解壓縮oscache-2.2-full.zip后把oscache-2.2.jar拷貝到應用的WEB-INF/lib下 ,
               并把etc目下下的oscache.properties拷貝到應用的WEB-INF/classes下.
            3, 在應用的web.xml中加入緩存過濾器    
              
                 <filter>
                     
          <filter-name>CacheFilter</filter-name>
                     
          <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
                     
          <init-param>
                         
          <param-name>time</param-name>
                         
          <param-value>600</param-value>
                     
          </init-param>
                     
          <init-param>
                         
          <param-name>scope</param-name>
                         
          <param-value>application</param-value>
                     
          </init-param>
                 
          </filter>
                 
                 
          <filter-mapping>
                     
          <filter-name>CacheFilter</filter-name>
                     
          <url-pattern>/servlets/UserAllProducts</url-pattern>
                 
          </filter-mapping>

            以上的/servlets/UserAllProducts訪問需要操作數據庫,并且這些內容一段時間內很少改變,這樣在內存緩存這個URL很有必要
            它可以降低數據庫訪問量。
           
            經過以上配置后,當第一次訪問/servlets/UserAllProducts時,從數據庫中查出所有產品列表并緩存到application中600秒。
            在600秒內的任何訪問/servlets/UserAllProducts都不會真正執行這個servlet,而直接從application中取出緩存的內容。這樣
            就減少了數據庫的訪問量。
          posted @ 2006-02-09 18:58 java小記 閱讀(291) | 評論 (0)編輯 收藏

          今天遇到了這樣的問題,就是浮點運算后數據比較出現錯誤,郁悶了半天,網上查了資料才發現浮點數直接用雙目運算符連接會出現結果不準確問題。解決方法如下:
          1。所有浮點運算都在數據庫內做好,也就是都用sql實現了
          2。用BigDecimal實現,方法如下(僅僅是個例子):

          import java.math.BigDecimal;

          public class tt {

           
          /**
            * 
          @param args
            
          */

           
          public static void main(String[] args) {
            
          float a = 1.1f;
            
          float b = 2.2f;
            tt t 
          = new tt();
            System.out.println(t.add(a,b));
            System.out.println(t.sub(a,b));
            System.out.println(t.mul(a,b));
            System.out.println(t.div(a,b));
            System.out.println(t.round(a));

           }

           
          public float add(float v1,float v2){//加法
             BigDecimal b1 = new BigDecimal(Float.toString(v1));
             BigDecimal b2 
          = new BigDecimal(Float.toString(v2));
             
          return b1.add(b2).floatValue();
            }


            
          public float sub(float v1,float v2){//減法
             BigDecimal b1 = new BigDecimal(Float.toString(v1));
             BigDecimal b2 
          = new BigDecimal(Float.toString(v2));
             
          return b1.subtract(b2).floatValue();
            }


            
          public float mul(float v1,float v2){//乘法
             BigDecimal b1 = new BigDecimal(Float.toString(v1));
             BigDecimal b2 
          = new BigDecimal(Float.toString(v2));
             
          return b1.multiply(b2).floatValue();
            }


            
          public float div(float v1,float v2){//除法
             BigDecimal b1 = new BigDecimal(Float.toString(v1));
             BigDecimal b2 
          = new BigDecimal(Float.toString(v2));
             
          return b1.divide(b2,3,BigDecimal.ROUND_HALF_UP).floatValue();
            }


            
          public float round(float v){//截取3位
             BigDecimal b = new BigDecimal(Float.toString(v));
             BigDecimal one 
          = new BigDecimal("1");
             
          return b.divide(one,3,BigDecimal.ROUND_HALF_UP).floatValue();
            }

          }

          posted @ 2006-02-08 16:43 java小記 閱讀(277) | 評論 (0)編輯 收藏

          spring配置中bean的循環引用問題及解決方法

          問題:Spring+Hibernate的應用中,定義了兩個業務Service,這里分別稱它們為serivceA,ServiceB。
          它們的關系簡單點來說是這樣的:
          serviceA需要引用serviceB,在serviceB中定義了一個接口列表,serverA必須在serviceB初始化時設置進列表。
          在純bean的情況下,也就是這兩個類不需要設置其他bean的情況下,循環引用是正常的,可以通過的。例如下面配置所表示:

              <bean id="serviceA" class="A"  autowire="byName"  lazy-init="true">
               <property name="serviceB"><ref local="serviceB"/></property>
              </bean>
           <bean id="serviceB" class="B"  autowire="byName"  lazy-init="true">
               <property name="serviceA"><ref bean="serviceA"/></property>
           </bean>
          但是作為一個業務接口,它應該是不需要關心事務,回滾這些無關的東西,
          但現實又有這樣的需求,所以我們必須保證透明的實現這個功能,于是引
          入了AOP方式解決該問題,利用的是Spring自帶的org.springframework.t
          ransaction.interceptor.TransactionProxyFactoryBean.
          重新聲明文件如下:
             <bean id="baseTxProxy" lazy-init="true"
                class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
                  <property name="proxyTargetClass"><value>true</value></property>
                  <property name="transactionAttributes">
                      <props>
            <prop key="*">PROPAGATION_REQUIRED</prop>
                      </props>
                  </property>
              </bean>
               
              <bean id="serviceA" parent="baseTxProxy">
               <property name="target"><ref local="serviceAImpl"/></property>
              </bean>
             
             <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
               <property name="serviceB">
                   <ref bean="serviceB"/>
               </property>
             </bean>
             
              <bean id="serviceB" parent="baseTxProxy" lazy-init="true">
               <property name="target"><ref local="serviceBImpl"/></property>
              </bean>
            
             <bean id="serviceBImpl" class="D" lazy-init="true">
               <property name="serviceA">
                   <ref bean="serviceA"/>
               </property>
             </bean>
          于是問題就出現了,Spring報了FactoryBeanCircularReferenceException,無法繼續完成設置工作。
          查看TransactionProxyFactoryBean源碼,其實現了FactoryBean和InitializingBean接口,應該是
          做了代理之后,兩個代理Bean需要等待所有Bean設置完成后才會標識狀態為初始化完畢,于是造成了
          沖突。

              由于兩個業務服務互相調用的路徑是不相交的,所以采用了一種變通的方法,在聲明serviceA時,
          直接定義serviceB:
            <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
               <property name="serviceB">
                   <bean class="B"  autowire="byName"/>
               </property>
           </bean>
          相當于serviceB和serviceA中使用的serviceB不是同一個實例。
           
           但是如果確實調用重合時怎么辦?
           
           解決方法是這樣的:
           
           <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
               <property name="serviceB">
                   <ref bean="serviceBImpl"/>
               </property>
           </bean>
           
            非常簡單,serviceAImpl調用時,可能已經在事務環境中了,不需再使用serviceB代理的事務支持,
            于是直接引用serviceB實例。這個方法是我寫這篇文章時想到的,-_-!!!,看來知識果真還是好好
            整理呀。

           

          posted @ 2006-02-08 16:32 java小記 閱讀(898) | 評論 (0)編輯 收藏

          <2006年2月>
          2930311234
          567891011
          12131415161718
          19202122232425
          2627281234
          567891011

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 阜平县| 肥东县| 邵武市| 溆浦县| 泗阳县| 志丹县| 汨罗市| 平果县| 韶关市| 永川市| 桐柏县| 南阳市| 柳江县| 罗平县| 漾濞| 梅河口市| 驻马店市| 同心县| 侯马市| 吉安县| 句容市| 襄汾县| 文安县| 安顺市| 仁寿县| 衡南县| 花垣县| 淅川县| 塔城市| 新丰县| 阿巴嘎旗| 铜川市| 泰州市| 仙桃市| 航空| 阳谷县| 十堰市| 灵寿县| 南丰县| 田东县| 尼木县|