摘要: 對一個成規模的系統來說,緩存總是不可或缺的一個環節,甚至會成為系統成功的重要因素。
從原理來講,緩存并不神秘,它本質上只是一個哈希表,內部包含許多提取關鍵字和緩存內容的鍵值對,當有讀操作(如search)新的查詢到來時,系統先到
這個哈希表中看看是否有同樣的關鍵字存在,是則取出對應的值返回,否則進行查詢,并把新的查詢條件和結果存儲進哈希表,以便下次提取;當有寫操作(如
add,delet... 閱讀全文
當柳上原的風吹向天際的時候...真正的快樂來源于創造 |
摘要: 對一個成規模的系統來說,緩存總是不可或缺的一個環節,甚至會成為系統成功的重要因素。
從原理來講,緩存并不神秘,它本質上只是一個哈希表,內部包含許多提取關鍵字和緩存內容的鍵值對,當有讀操作(如search)新的查詢到來時,系統先到
這個哈希表中看看是否有同樣的關鍵字存在,是則取出對應的值返回,否則進行查詢,并把新的查詢條件和結果存儲進哈希表,以便下次提取;當有寫操作(如
add,delet... 閱讀全文
在交易系統的客戶端中,涉及的業務種類有很多,如分類,商品,詢價,協商等大類及其下的CRUD四個小類,每種操作幾乎都需要一個界面供用戶操作,這些界面及其內部處理如果安排不當會造成維護上的巨大麻煩,而且隨著程序的發展,界面表現,用戶交互和數據處理都會而變得越來越復雜,如果不在一開始就對它們進行分開處理,最后只能導致不可維護的結果,屆時悔之晚矣。只有邁出了這堅實穩固的一步,后面的成功才有可靠的保證。因此我采用了MVC2模式來將每個操作界面的界面表現,用戶交互和數據處理三部分分離,目的是為了提高系統的可維護性和可擴展性。下面是對交易系統采用的MVC2模式的簡要介紹,以便于負責業務的程序員掌握之。
一.MVC2模式的來源 這種模式首見于網絡程序,起初因為Model無法通知到Web程序的界面而發明.這種模式采用Controller做中介者,一方面取得View的輸入,然后交由Model層處理,之后再把返回的數據傳遞到View。在Web程序(典型例子如Struts1)中各層任務如下:View接受用戶輸入,并在并傳遞到Controller;Controller統一進行處理命令,交由Model處理具體的業務;進過處理Model更新后,Controller會選一個View并把Model內容傳遞(request,session)給它,然后View進行顯示. 二.MVC2的優缺點 MVC2相對MVC1優勢很明顯,首先Model和View無需繼承別的類,其次Model和View無需了解對方的存在,只需準備相應的接口而已,最主要的是,是進行了數據部分,視圖部分和控制部分三層的區分,程序的耦合度將大為降低,而可讀性和可修改性將隨之提高;缺點對于規模較小的程序或是組件,MVC2稍顯費事,有些過度設計的嫌疑。 三.在客戶端程序中MVC各層擔負的職責 視圖View:它擔任數據的顯示并接受用戶的輸入,其中不包括業務邏輯,但可以包含顯示邏輯。 數據中心Model:它保存數據層和控制層所需要的數據并提供操作這些數據的方法。 控制器Ctrl:它是一個中介者,負責實例化視圖和數據中心,由此View和Model并不需要知道對方的存在。View的事件在Ctrl中注冊,這樣當事件發生時Ctrl能調用Model取得相應的數據并顯示在View中。Ctrl中包含處理邏輯。 下面的UML圖形可以參考: MVC2靜態類圖 ![]() MVC2順序圖 ![]() 下面是最簡化的MVC三層次代碼示例: 控制類 public class Mvc2Ctrl {
private Mvc2View view; private Mvc2Model model; public Mvc2Ctrl() { view = new Mvc2View(); model = new Mvc2Model(); handleEvents(); } // 處理事件響應 private void handleEvents() { addCloseLintener(); addButtonListener(); addButtonListener2(); } // 窗體關閉事件相應 private void addCloseLintener() { view.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.out.println("Exit MVC2"); System.exit(0); } }); } private void addButtonListener() { view.getButton().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { view.getLabel().setText(model.getText()); } }); } private void addButtonListener2() { view.getButton2().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { view.getLabel2().setText(model.getText2()); } }); } } 視圖類 public class Mvc2View extends JFrame {
模塊類private static final long serialVersionUID = 621145935910133202L; private JButton button; private JLabel label; private JButton button2; private JLabel label2; public Mvc2View() { locateView(300, 200); this.setTitle("MVC2 Program"); setupComponents(); this.setVisible(true); } // 定位程序在屏幕正中并設置程序大小 private void locateView(int width, int height) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); this.setSize(width, height); this.setLocation(screenSize.width / 2 - width / 2, screenSize.height / 2 - height / 2); } // 初始化內部組件 private void setupComponents() { button = new JButton("點擊響應事件1"); label = new JLabel(" 等待事件響應1"); button2 = new JButton("點擊響應事件2"); label2 = new JLabel(" 等待事件響應2"); setLayout(new GridLayout(2, 2)); add(button); add(label); add(button2); add(label2); } public JButton getButton() { return button; } public JButton getButton2() { return button2; } public JLabel getLabel() { return label; } public JLabel getLabel2() { return label2; } } public class Mvc2Model{
public String getText(){ return " 事件1響應完畢"; } public String getText2(){ return " 事件2響應完畢"; } } 小結: 一.越是大型程序,MVC越有必要;倒是小型程序不一定硬要遵守MVC。 二.以上代碼抽象程度還不夠,理論上說,應該阻止程序員在界面中寫監聽代碼。 三.三部分應該實現相應的接口,這里是應該繼續進化的地方。
在交易系統的C/S體系中,C只負責數據的輸入和顯示,相當于MVC中的View部分,S負責數據的操作和持久化,兩者是通過WebService進行聯系的,具體來說聯系的方式是這樣:C端將指定S端負責處理的Service類名,具體負責處理的函數名和函數的參數打包成一個XML傳送到S端,S端解析后通過反射找到具體的函數進行處理,處理的結果會轉化成XML形式的字符串傳回。這就是設計梗概一中提到的內容。
如果這個過程交給負責具體業務的程序員自行完成的話,那無疑會給系統帶來許多混亂和無序,程序員也無法將主要精力集中在業務上;另外,他們也無需了解每個細節是怎么完成的,他們真正需要的是框架提供好的接口,知道怎么調用取得結果就可以了。他們希望最好能像調用普通函數一樣調用S中的方法并取得想要的結果,舉例來說,如果客戶端需要查詢姓名以H開頭的所有雇員,他們只需調用一個search函數就能得到查詢出來的雇員集合,中間發生的組裝請求XML,WebService調用,業務處理,從數據庫查詢數據,將數據轉為XML傳回和在客戶端解析傳回的XML再變成領域對象集合等都應該由框架來完成。這并不過分,而是很合理的需求,就像RMI曾經就是這樣做的。 那么,客戶端與服務器端的交互會有幾種形式呢,從業務上來說無外乎下面五種形式: 1.調用S端的一個函數,只想知道這個函數是否正確運行了。典型例子如對象的刪除操作。 2.調用S端的一個函數,想得到函數執行后返回的一個對象。典型例子如對象的添加操作,用戶需要取回添加好的對象的ID。 3.調用S端的一個函數,想得到返回對象的集合列表。典型例子如對象的查詢。 4.調用S端的一個函數,想得到分頁后的某一頁對象集合。典型例子如分頁查詢。 5.調用S端的一個函數,只想得到一個字符串。典型例子如改變一種商品的目錄,得到某種商品的介紹文字等。 框架需要做的,就是把這五種形式做成通用的函數提供給負責業務的程序員,讓他們僅需要這五個函數就能完成與WebService服務器的交互。這五種形式以第二種最為典型,也最為基礎,完成了它其它的就可以依樣畫葫蘆,下面請看具體過程: 首先,規定具體函數的形制為 public static BaseDomainObj fetchObject(String url,String serviceName,String mothodName,String[] args,Class<?> cls); 公有靜態自不必說,BaseDomainObj是客戶端領域對象的基類,fetchObject是函數名,接下來是四個參數,前三個分別是WebService所在URL,服務器上服務類注冊在Spring上下文中的beanName,服務類具體的方法名,最后一個是取得對象的類型,在函數體中,會根據類型用反射生成一個實例,再通過實例的FromXML方法給實例的屬性賦值,完成后就得到了負責業務的程序員想要的結果。 其次,fetchObject內部需要做的事情有: 1.將serviceName,mothodName,args三項組合成一段XML文本。此項工作由WSRequest類完成。 2.向位于url上的WebService服務器端發起請求,獲得返回的文本。此項工作由WSInvoker類來完成。 3.將返回的文本轉化出來,這一步是要檢測服務器端函數執行是否順暢,有無拋出異常等。因為服務器端如果發生異常是無法通過WebService傳回的,只能變成文本后回傳,那么客戶端就需要解析一次,有問題就報出來,沒問題再往下走。這一步是堅決不能忽視的。 4.通過反射得到對象,此時對象的屬性還是原始狀態,需要再通過反射注入相應的值,最后,客戶端需要的對象就產生了。 具體的過程請參考下面的代碼: /**
* 調用遠程WebService端,將返回的XML轉化為一個對象,最終返回.這種調用的典型例子是getById,add等 * @param url WebService所在URL * @param serviceName 服務名,此名在appCtx.xml中定義 * @param mothodName 方法名 * @param args 方法的參數 * @param cls 要返回的對象類型 * @return */ public static BaseDomainObj fetchObject(String url,String serviceName,String mothodName,String[] args,Class<?> cls){ // 得到客戶端請求XML文本 WSRequest request=new WSRequest(serviceName,mothodName,args); String requestXML=request.toXML(); logger.info("準備向位于'"+url+"'發送的請求XML為'"+requestXML+"'."); try{ // 調用遠端WebService上的方法,得到返回的XML文本 WSInvoker invoker=new WSInvoker(url); String responseXML=invoker.getResponseXML(requestXML); logger.info("得到位于'"+url+"'響應XML為'"+responseXML+"'."); // 轉化響應 WSResponse response=new WSResponse(responseXML); logger.info(response); // 如果在調用過程中如通不過檢測而被中斷的話 if(response.isBreaked()){ String errTxt="遠程方法被中斷,具體原因是"+response.getRemark(); logger.error(errTxt); throw new WSBreakException(errTxt+".(WSE05)"); } // 如果在調用過程中出現異常的話 if(response.hasException()){ String errTxt="調用遠程方法返回了異常,具體信息是"+response.getRemark(); logger.error(errTxt); throw new WSException(errTxt+".(WSE04)"); } try{ // 通過反射得到對象 BaseDomainObj obj= (BaseDomainObj)cls.newInstance(); // 通過反射得到方法 Method method = cls.getMethod("fromXML", new Class[] {String.class}); // 通過反射調用對象的方法 method.invoke(obj, new Object[] {response.getMethodResonseXML()}); return obj; } catch(Exception ex){ String errTxt="無法將"+response.getMethodResonseXML()+"轉化為"+cls.getName()+"對象.(WSE06)"; logger.error(errTxt); throw new WSException(errTxt); } } catch(MalformedURLException e){ String errTxt="無法調用'"+url+"'上的服務,因為它是畸形的.(WSE01)"; logger.error(errTxt); throw new WSException(errTxt); } catch(XFireRuntimeException e){ String errTxt="無法調用'"+url+"'上的服務.(WSE02)"; logger.error(errTxt); throw new WSException(errTxt); } catch(DocumentException e){ String errTxt="無法解析從服務器端'"+url+"'返回的XML文本.(WSE03)"; logger.error(errTxt); throw new WSException(errTxt); } } 我們再看看通過關鍵的注入屬性值的fromXML函數做了些什么: public void fromXML(String xml) throws DocumentException{
Document doc=DocumentHelper.parseText(xml); Element root=doc.getRootElement(); List<Element> elms=root.elements(); for(Element elm:elms){ try { // 借助于BeanUtils,給對象的屬性賦值 BeanUtils.setProperty(this,elm.getName(),elm.getText()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } 最后,我們可以看看負責業務的程序員需要書寫的代碼示例: public Tmp add(String name,String age,String salary,String picture){
String url=CommonUtil.WebService_Url; String serviceName="TmpService"; String methodName="add"; String[] args=new String[]{name,age,salary,picture}; try{ return (Tmp)WSUtil.fetchObject(url, serviceName, methodName, args, Tmp.class); } catch(WSBreakException ex){ DlgUtil.popupWarningDialog(ex.getMessage()); } catch(WSException ex){ DlgUtil.popupErrorDialog(ex.getMessage()); } return null; } 小結: 一.負責業務程序員不需要了解的細節,框架應該將它們隱藏起來。 二.負責業務程序員需要了解的接口,框架應該使它們盡量簡單。 三.框架能做到的,就不該再讓負責業務的程序員再重復的發明車輪。
下載地址:
http://www.box.net/shared/o48fxqem61 SqlToolBox是一款純綠色的免費數據庫客戶端軟件,基于Java Swing編制而成,旨在于為開發人員,系統工程師和數據庫管理員提供一種通用方便和快捷的數據庫操作工具,使他們擺脫需要學習掌握使用多種數據庫客戶端 的苦惱,并減輕他們日常操作數據庫和編寫Sql語句的任務量,幫助他們把精力投入到解決更有意義的問題上去。 SqlToolBox現有功能
運行SqlToolBox有何前提條件?將SqlToolBox運行起來的唯一前提是安裝JDK6或以上版本。SqlToolBox需要安裝嗎?SqlToolBox是一款純綠色軟件,它對您的系統不做出任何更改,因此不需要安裝和卸載。SqlToolBox安全嗎?由于軟件使用Java編寫而成,它本身就具有較高的安全性。此外作者保證在SqlToolBox整個系列中都不會加入病毒,木馬,插件等壞東西。如何運行SqlToolBox?解開下載包,然后雙擊run.bat即可。在Unix/Linux下如何運行SqlToolBox?除了也需要安裝JDK外,您還需要參照run.bat寫一份腳本,然后執行它。如何使用SqlToolBox打開一個數據庫?程序運行起來后,您將看到一個輸入數據庫信息的對話框,請依次填入數據庫所在機器的IP地址,數據庫的庫名稱,選擇數據庫的類型以及輸入登錄數據庫的用戶 名和密碼等必要信息。此后再點擊“連接數據庫”按鈕,程序將打開數據庫。如果您將以上信息填錯也不要緊,程序會提示哪里出現了問題。此外您可以在登錄前點 擊“測試連接”按鈕,程序也會告訴您是否能連接到指定的數據庫。打開數據庫后程序左邊部分如何使用?成功連接到數據庫以后,數據庫的Schema和table結構會在畫面的左邊以樹的形式展現出來,如果展現的內容過多,您還可以在上方的“過濾器”輸入欄 中輸入關鍵字以縮小展現范圍。在這顆樹中,表格(table)是以小圓點的方式展現的,左鍵點擊這個圓點,程序將在右側打開一個Sql語句操作窗口,并執 行“select * from table”語句,最后在下方以表格的形式展現給您;如果您用右鍵點擊這個圓點,程序將彈出一個右鍵菜單,選擇其中的項目您將可以在右邊的Sql語句操作 窗口中得到單表的字段信息,創建(insert),查詢(select),更新(update),刪除語句(delete)及建表語句(create table),單表對應Pojo文件,單表的Hibernate映射文件等文字。打開數據庫后程序右邊部分是如何使用的?用左右鍵點擊表格后,您將在右側看到一個“Sql語句操作窗口”,它分成三部分:工具欄菜單,輸入窗口和輸出窗口。輸入窗口是用以輸入,編輯和整理Sql 語句的;工具欄菜單中的一大排按鈕都是為編輯處理輸入窗口中的文字而準備的;輸出窗口則是展示Sql語句執行后的結果的,如果是查詢語句,它會以表格的形 式告知您查詢的結果,如果是其它語句,它會以文字的形式告知。通常的操作手法是,先在輸入窗口中用鼠標選中您要操作的文本,再在工具欄菜單中點擊執行特定 操作的按鈕,然后在下方的輸出窗口中就能看到具體的結果,當然如果僅是文本編輯操作的話輸出窗口是不會有反應的。如何執行Sql語句?程序員和數據庫管理員總是習慣使用語句來操作數據庫,這也是本軟件的最重要功能之一。執行Sql語句的過程具體來說是這樣做的,首先,在輸入窗口輸入您向 執行的Sql語句,如“select * from table”之類,當然您更可以通過表格的右鍵菜單來獲得常用的sql語句(在輸入或是粘貼文本的過程中,Sql語句中的關鍵字會以藍色顯示,這是語法高 亮功能所致);其次,你需要選中你想執行的文本,再點擊工具欄菜單中的向右三角形按鈕,這段文本將得到執行,執行結果將在下方的輸出窗口得到展示。如果您 執行的是查詢語句,輸出窗口將以表格的形式列出查詢結果集的字段和內容;如果您執行的是刪除,更新,添加,修改表等語句或是執行錯誤的Sql文本,輸出窗 口將以文本形式告知執行結果。另外工具欄菜單中的雙向右三角形按鈕用于批量執行Sql語句,它以分號“;”來作為每段Sql的分隔標志,然后分別執行每 段。如何快速調整對執行查詢語句后得到的表格列寬度?如果您想自動調整某列的寬度,可以雙擊這列的表頭,此后這列的寬度會根據這列的最長文字進行調整;您還可以在表格上點擊右鍵,選擇“調整列寬為最適合狀態”一項,那么所有的列寬都會進行調整。如何得到執行查詢語句后得到的表格的內容?您還可以在表格上點擊右鍵,選擇“下載表格為CSV文件”一項,此后查詢語句和得到的結果都會被放入一個CSV文件中。CSV是一中文本文件,但您可以用Excel打開它,也會得到xls文件一樣的效果。在新增或是刪除一張表后,在左邊的樹中為什么沒有相應的變化?新增或是刪除一張表后,您需要手動執行一下左上方的更新按鈕(最上方的大圖標中第一個),此后程序會重新載入數據庫的Schema和table,這樣您剛才對表格進行增刪操作就能體現出來。如果我需要常打開數據庫進行操作或是需要常操作多個數據庫,程序能為我提供那些便利?本軟件有記憶功能,如果您正確連接到一個數據庫,那么相應的信息如IP地址,數據庫名,數據庫類型,連接數據庫的用戶名和密碼都會被記憶下來,這樣下次打 開時就不用重復輸入了。如果您需要常操作多個數據庫,您可以通過保存按鈕(最上方五個大圖標中的第二個)將數據庫信息保存成XML文件,這樣在登錄畫面中 就可以通過“打開文件按鈕”得到相應的數據庫信息。程序截圖: ![]() ![]() ![]() ![]() ![]() ![]()
此項目是一個采購商和供貨商之間的交易軟件,雙方可以在系統中發布供求信息,應答供求信息,與對方談判敲定供求細節如品種,數量,交貨地點,交貨批次等,此外還有產品展示,信用記錄等交易系統常用功能。
此系統難點在于大量即時信息的送達和交互信息的多樣化,框架中采用了JMS和WebService兩種手段來保證這兩項的成功,并擬采用IM服務器來取代JMS。在層次上采用了WebService來屏蔽后臺數據庫,并用XML來傳遞前后臺信息。由于采用了使用Java反射技術的控制中心方案和基于AOP的緩存方案,有效降低了系統的開發難度,使得系統的可擴展性和可維護性得到了較大提高。 項目使用的技術: 前臺技術:Swing,XML,WebService,JMS。 后臺技術:JMS,WebService,Spring,Hibernate等。
摘要: 我在前一向做交易系統時的設計梗概之一,歡迎拍磚。 閱讀全文
摘要: 如果有效利用XML和反射等手段,我們可以做到工具欄菜單項的可配置化。具體來說就是,將菜單項的文字,圖片和點擊后的響應函數都在XML配置文件中配置好,程序啟動時去讀取文件生成菜單,點擊菜單項后會動態的找到具體需要處理的函數。這樣做以后,修改一個按鈕對應的功能定位代碼,或是增刪一個按鈕及其功能就很容易了。 閱讀全文
XML文件如下:
<?xml version="1.0" encoding="GBK"?>
<menuItems> <name>根節點</name> <icon></icon> <panelName></panelName> <mothodName></mothodName> <args></args> <menuItem> <name>節點一</name> <icon>folder_fromFile.gif</icon> <panelName> com.heyang.view.panel.content.folder.addfromfile.AddCategoryFromFilePanel </panelName> <mothodName></mothodName> <args></args> </menuItem> <menuItem> <name>節點二</name> <icon>folder_attribute.gif</icon> <panelName> com.heyang.view.panel.content.folder.attribute.AttributeMngPanel </panelName> <mothodName></mothodName> <args></args> </menuItem> <menuItem> <name>節點三</name> <icon>folder_transfer.gif</icon> <panelName> com.heyang.view.panel.content.folder.transfer.FolderTransferPanel </panelName> <mothodName></mothodName> <args></args> </menuItem> </menuItems> 讀取例程: /**
* 按照XML文件建立一棵樹 * * @author:何楊 * @date:2009-12-22 * @time:上午08:13:09 */ @SuppressWarnings("unchecked") private void buildTree(){ // 建立樹節點 DefaultMutableTreeNode root = null; try { SAXReader reader = new SAXReader(); String xmlFile = TreeMenuPanel.class.getResource("/text.xml").getPath() .toString(); File file=new File(xmlFile); if(file.exists()==false){ DlgUtil.popupErrorDialog("無法找到文件"+xmlFile+"."); return; } Document document = reader.read(file); Element rootElm = document.getRootElement(); // 遍歷XML生成節點樹 root=getNode(rootElm); } catch (Exception ex) { ex.printStackTrace(); } // 將節點樹賦予樹組件 DefaultTreeModel model = (DefaultTreeModel) tree.getModel(); model.setRoot(root); model.reload(); tree.updateUI(); } /** * 遞歸取得節點 * * @author:何楊 * @date:2010-1-11 * @time:上午08:31:12 * @param elm * @return */ @SuppressWarnings("unchecked") private DefaultMutableTreeNode getNode(Element elm){ String name=elm.elementText("name"); String icon=elm.elementText("icon"); String panelName=elm.elementText("panelName"); String mothodName=elm.elementText("mothodName"); String args=elm.elementText("args"); MenuItem menuItem=new MenuItem(name,icon,panelName,mothodName,args); DefaultMutableTreeNode leaf = new DefaultMutableTreeNode(); leaf.setUserObject(menuItem); List<Element> elms = elm.elements("menuItem"); for (Element element : elms) { leaf.add(getNode(element)); } return leaf; } 1.1 創建樹組件 ![]() ![]() 在顯示時,樹組件會帶上JTree默認的節點。 1.1.2 指定樹的節點后創建樹: ![]() ![]() ![]() ![]()
![]() ![]() ![]() ![]() DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root =(DefaultMutableTreeNode)model.getRoot();
1.2.2 更新樹的根節點 DefaultMutableTreeNode newRoot=
![]() DefaultTreeModel model = (DefaultTreeModel) tree.getModel(); model.setRoot(newRoot); model.reload(); tree.updateUI();
1.2.3 從一個節點開始遞歸遍歷其下的所有節點 private String getNodeText(DefaultMutableTreeNode node){
Category category=(Category)node.getUserObject(); StringBuilder sb=new StringBuilder(category.getText()); if (node.getChildCount() >= 0) { for (Enumeration<?> e=node.children(); e.hasMoreElements(); ) { DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)e.nextElement(); sb.append(getNodeText(childNode)); } } return sb.toString(); }
1.2.4 在某節點下創建一個節點 DefaultMutableTreeNode childNode = new DefaultMutableTreeNode();
childNode.setUserObject( ![]() parentNode.add(childNode); 1.2.5 節點改名 Category selectedCategory=(Category)clickedNode.getUserObject();
selectedCategory.setName(" ![]() clickedNode.setUserObject(selectedCategory);
1.2.6 刪除節點 DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
model.removeNodeFromParent(clickedNode); 注意被刪除的節點必須有父節點,若要刪除根節點直接用null去替代原有根節點即可, 1.2.7 遍歷用戶選擇的樹節點 TreePath[] paths = tree.getSelectionPaths();
for(TreePath selPath:paths){ Object[] nodes = selPath.getPath(); DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes[nodes.length - 1]; // 對node進行處理 }
1.3 樹的事件處理 tree.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) { int selRow = tree.getRowForLocation(e.getX(), e.getY());// 返回節點所在的行,-1表示鼠標定位不在顯示的單元邊界內 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());// 返回指定節點的樹路徑 boolean condition = true; condition = condition && (selRow != -1);// 如果選中 //condition = condition && (e.getButton() == 1);// 左鍵 e.getButton() == 1 右鍵 e.getButton() == 3 condition = condition && (e.getClickCount() == 1);// 單擊 // 如果是左鍵點擊 if (condition == true && (e.getButton() == 1)) { Object[] nodes = selPath.getPath(); DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes[nodes.length - 1]; Category selectedCategory=(Category)node.getUserObject(); ![]() ![]() } } });
1.3.2 在樹節點上點擊右鍵彈出右鍵菜單 tree.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) { int selRow = tree.getRowForLocation(e.getX(), e.getY());// 返回節點所在的行,-1表示鼠標定位不在顯示的單元邊界內 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());// 返回指定節點的樹路徑 boolean condition = true; condition = condition && (selRow != -1);// 如果選中 condition = condition && (e.getClickCount() == 1);// 單擊 // 如果是右鍵點擊 if(condition == true && (e.getButton() == 3)){ Object[] nodes = selPath.getPath(); DefaultMutableTreeNode rightClickedNode = (DefaultMutableTreeNode) nodes[nodes.length - 1]; ![]() ![]() popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } }); 1.4 樹的渲染 public class CategoryNodeRenderer extends DefaultTreeCellRenderer{
private static final long serialVersionUID = 8532405600839140757L; private static final ImageIcon categoryLeafIcon = new ImageIcon(CategoryNodeRenderer.class.getResource("/categoryLeaf.gif")); private static final ImageIcon openFolderIcon = new ImageIcon(CategoryNodeRenderer.class.getResource("/openFolder.gif")); private static final ImageIcon closedFolderIcon = new ImageIcon(CategoryNodeRenderer.class.getResource("/closedFolder.gif")); public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus){ super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if(leaf){ setIcon(categoryLeafIcon); } else if(expanded){ setIcon(openFolderIcon); } else{ setIcon(closedFolderIcon); } return this; } }
1.4.2 用樹節點渲染器渲染一棵樹 DefaultMutableTreeNode root = null;
JTree tree = new JTree(root); tree.setCellRenderer(new CategoryNodeRenderer());
當不需要全體字段或是不必要的字段屬性映射匹配影響效率時,我們可以使用在HQL語句中直接返回一個Java對象,如下:
select new com.heyang.domain.Folder(id,pid,name) from Category c 需要注意的是: 1.Folder類應該寫全路徑名,如上面的com.heyang.domain.Folder,否則Hibernate會說Unable to locate class ‘Folder’。 2.Folder類應該具有一個和參數相匹配的構造函數,如果上面的id,pid,name三個字段的類型分別是long,long,vchar,那么Folder類的構造函數應該是Folder(Long op1,Long op2,String op3);的形式。 就是這樣,很簡單,簡單到Hibernate幫助手冊都對之語焉不詳,寫出來注意一下就好了。 |