1. <tr id="722gv"></tr>

            實作主從維護頁面

            2024-03-07 18:39 更新

            在前面實做單表CRUD的過程中,我們并沒有使用的立體數據模型,不過在這個過程中我們對基于數據模型的開發有了一個初步的了解,下面我們基于一個主從表維護的工作來實際體驗立體數據模型的開發過程。 訪問如下的URL:??http://dorado.bstek.com/sample-center/com.bstek.dorado.sample.data.MasterDetail.d 我們可以在瀏覽器中看到如下的視圖: 這是一個主從表的維護界面,主表顯示產品類別,從表表示對應產品類別的產品列表。當我們選擇上面這個Grid的不同產品類別的時候,下面Grid會自動的顯示相對產品類別的產品列表。如果我們用過Dorado5技術,就會知道做這么一個頁面并不復雜,我們只要在頁面上定義兩個DataSet對象,并采用主從關系的MasterLink技術做好關聯設定,就可以開發出這個頁面。而在Dorado中我們只要采用一個DataSet就可以完成開發,從技術上來說我們可以認為它就是一個立體數據,第一層是產品分類,每一個產品分類下有不同的產品。 下面我們根據SampleCenter中的范例了解立體數據的開發技術,首先打開MasterDetail.view.xml: 首先我們關注DataType的定義,其中的parent屬性,在該處被定義為:"global:Category",parent屬性是告訴Dorado該DataType的繼承關系,其中global關鍵字是表明這個parent的DataType是一個全局的DataType。否則如果我們parnet屬性只配置為"Category",它就會認為這是一個私有的DataType。全局DataType都是定義在系統默認的models下,我們找到這個全局的DataType: 在視圖中我們可以看到Category的定義,在本例中為了說明DataType支持繼承而專門做了復雜的繼承關系(實際上并不一定需要設計這種繼承關系),其中Category繼承BaseCategory,BaseCategory繼承CommonEntity,在本例中CommonEntity的作用是申明一個名稱為id的propertyDef,并設置了其required屬性為true,這樣在繼承的BaseCategory,Category等DataType中就不需要關心ID的設置,自動繼承了這個特性: 前面我們說過,頁面的邏輯是選擇不同的產品分類,下面的Grid顯示不同的產品列表,對于DomainObject來講這是一個引用關系,是一個立體結構的數據。我們在DomainObject的定義中就能看到這個引用關系:

            package com.bstek.dorado.sample.entity;
            
            
            @Entity
            @Table(name = "CATEGORIES")
            public class Category implements Serializable {
                private static final long serialVersionUID = 6076304611179489256L;
            
            
                private long id;
                private Category parent;
                private String categoryName;
                private String description;
                private Collection<Category> categories;
                private Collection<Product> products;
            
            
                ...省略
                @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
                @JoinColumn(name = "CATEGORY_ID")
                public Collection<Product> getProducts() {
                    return products;
                }
            
            
                public void setProducts(Collection<Product> products) {
                    this.products = products;
                }
            }

            其中的products就是Category的產品列表。那么我們就容易理解Category中的中名稱為products的PropertyDef的定義了,此處的products中我們注意到未設定dataType屬性,這是用到了DataType的一個特性,這個特性我們在DataType的說明文檔中的其他特性中提到DataType與JavaBean的一一映射關系,而在Demo.model.xml中定義了名稱為Product的DataType對象: 可以看到Product對象設定了matchType,這樣com.bstek.dorado.sample.entity.Product就與Product這個全局的DataType建立了一一對應的映射關系,在Category中雖然我們沒有定義products的DataType屬性,但Category對象根據自身的引用關系:

            private Collection<Product> products;

            知道這是一個Product的集合,那么根據前面的一一映射關系,就很容易的知道這個默認的DataType為Demo.model.xml中定義的那個全局的Product。 如果沒有特殊的必要,不要在全局的Model文件中建立對象之間的關系,而是應該在自身的View中去建立,為什么呢?這是因為如果我們在Model中就直接建立了這個關聯關系,但是我們不知道最終有多少個視圖會需要這種關系,這樣導致任何一個視圖引用這個全局DataType的時候都得到一個立體的數據對象,而這對很多視圖來說可能并不是必須的。另外這種立體視圖也可能會導致持久層不必要的數據加載從而性能損耗。 了解了DataType的基本設定之后,我們再來看DataSet的設定,如下圖: 有了前面單表CRUD的了解,對其中dataType和dataProvider的設定都比較熟悉了,根據dataProvider的設定,也很容易的可以找到對應的Java代碼:

            package com.bstek.dorado.sample.interceptor;
            
            
            @Component
            public class CategoryInterceptor {
            
            
                @Resource
                private CategoryDao categoryDao;
            
            
                @DataProvider
                public Collection<Category> getAll() {
                    return categoryDao.getAll();
                }
            ...省略

            其中的getAll方法用以向外返回一個產品分類列表。應該注意到這兒我們沒有再返回product對象,這是因為我們以及在DataType中添加的products屬性了,這樣Dorado試圖解析Category數據的時候就會自動的讀取products屬性,同時我們注意的Category.java對products的裝載設定:

            @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
            @JoinColumn(name = "CATEGORY_ID")
            public Collection<Product> getProducts() {
                return products;
            }

            這兒采用了LAZY的裝載方式,一旦試圖讀取category的products屬性的時候,hibernate就會幫助我們自動的裝載對應的產品列表。 好了到目前為止我們已經完成了最重要的工作:通過定義一個DataSet我們可以拿到一個立體數據模型的Category列表了。接下來的工作就比較簡單了,只要在頁面上放置一些數據敏感控件,把它關聯到立體數據模型上的不同節點下就可以。下面我們來了解一下數據敏感控件的使用,我們來看一下View的定義,在SplitPanel控件中我們看到有兩個Grid,分別用來展示產品分類和產品列表,其中展示Category的Grid的定義比較簡單,我們已經比較熟悉了,設定一下dataSet屬性就可以: 我們主要關注Product的Grid的定義: 這個Grid除了指定dataSet屬性之外,還定義了dataPath屬性"#.products"。這個表達式的含義是:"#":表示當前記錄,連起來的意思是"當前Category記錄下的products"。 當前記錄是什么意思呢? 就是當我們在Category那個Grid選擇不同行的時候,當前行的背景色會變綠,選中的這一行就是當前記錄,當我們在瀏覽中選擇不同的行操作時,這個#代表的記錄會實時的變化,而這樣"#.products"就代表了當前選中行的產品列表,它也是動態變化的。好了這就是這兩個Grid的重要屬性設定。 通過這個例子我們了解了立體數據模型的使用,另外我們還接觸到了一個新的技術:DataPath,這個我們將在下一章中再詳細介紹。

            性能優化

            剛才的范例,如果我們分析其HTTP請求,通過Chrome的F12打開Developer Tools,并重新刷新頁面后,查看Developer Tools中的AJAX請求,找到其中view-service的AJAX請求,并切換標簽也到Response:

            仔細分析Response的代碼:

            <?xml version="1.0" encoding="UTF-8"?>
            <result>
            <request>
            <response type="json"><![CDATA[
            {
                "data":[
                    {
                        "id":1,
                        "categoryName":"Beverages",
                        "products":[
                            {
                                "id":1,
                                "productName":"Chai",
                                "categoryId":1,
                                "discontinued":false,
                                "quantityPerUnit":"10 boxes x 20 bags",
                                "reorderLevel":12,
                                "unitPrice":18.0,
                                "unitsInStock":39,
                                "unitsOnOrder":0
                            },
                            {
                                "id":2,
                                ...
                            },
                            ...省略多個Product
                        ],
                        "description":"Soft drinks, coffees, teas, beers, and ales"
                    },
                    {
                        "id":2,
                        "categoryName":"Condiments",
                        "products":[
                            {
                                "id":3,
                                "productName":"Aniseed Syrup",
                                "categoryId":2,
                                "discontinued":false,
                                "quantityPerUnit":"12 - 550 ml bottles",
                                "reorderLevel":66,
                                "unitPrice":10.0,
                                "unitsInStock":13,
                                "unitsOnOrder":70
                            },
                            {
                                "id":4,
                                ...
                            },
                            ...省略多個Product
                        ],
                        "description":"Sweet and savory sauces, relishes, spreads, and seasonings"
                    },
                    ...省略多個Category
                ],
                "$dataTypeDefinitions":[
                ],
                "$context":{
                }
            }
            ]]></response>
            </request>
            </result>

            簡單的閱讀一下代碼,我們不難發現這個AJAX請求將立體數據模型中的產品分類和產品列表的所有信息都下載到客戶端了。那么這兒存在一個問題,如果產品分類特別多,比如1000個產品分類,同時每個產品分類下有1000個產品,如果按這種機制處理的話這一個AJAX請求會產生100W個產品信息的數據下載,這是不可想象的。雖然我們列舉了一種比較極端的情況,但這也說明了一個問題,對于立體數據模型的下載,我們還是有性能上的考慮的,這也對軟件開發人員提出了要求:就是開發的初期軟件開發人員要大概預期可能的數據量,再匹配相對合理的開發模式,如該分頁的時候分頁,該懶加載的時候懶加載。下面我們看針對本例中的功能實現懶加載實現的一個處理,在SampleCenter中已經實現了這個范例,我們大概預覽一下這個頁面: 頁面鏈接:http://dorado.bstek.com/sample-center/com.bstek.dorado.sample.data.MasterDetailLazy.d 頁面效果: 界面效果完全一樣,唯一不同之處是在我們選擇不同的Category的時候,可以在界面的右上角看到一個提示框。當我們切換不同的Category的時候都可以看到這么一個提示框,很容易就看出這就是懶加載的一種效果,多次單擊不同的Category后,我們再將瀏覽器切換到Developer Tools中就可以看到很多的View Service請求,打開其中的一個查看Response信息: 分析其中的數據不難發現,現在每一次都只是下載當前Category對應的產品列表。通過這種懶加載處理機制就能很好的解決我們之前說的1000個產品分類,每個產品分類有1000個產品的性能問題。 下面再來看看實現懶加載的開發與之前有什么不同之處,首先打開對應的視圖配置文件:MasterDetailLazy.view.xml 其不同之處在于其中Category這個DataType下的products為橙色,而原來的MasterDetail.view.xml下為綠色: 他們之間的差別是什么呢?我們選擇Category這個DataType,注意看IDE中的工具欄,其中DataType下可以添加三個元素:PropertyDef, Lookup, Reference 上圖我們看到的綠色的代表PropertyDef, 橙色的代表Reference。 接下來我們就介紹這兩種對象之間的差別。 PropertyDef可以認為就是一個bean的引用,但是Reference相對來說就是一種松散的關系,它不要求Bean內部包含這種邏輯關聯關系,而可以直接在View中的Dorado層面建立這種關系。我們來看看Reference的基本屬性設定: 其中可以定義dataProvider屬性,由于Reference不要求通過Bean本身的引用建立關系,可以直接利用Reference建立兩個Bean之間的關聯。它允許通過Reference本身定義當前屬性的數據來源。數據的獲取是通過DataProvider得到的,這個dataProvider屬性我們已經很熟了,我們找到相關的Java方法:

            package com.bstek.dorado.sample.interceptor;
            
             
            @Component
            public class ProductInterceptor {
                ..省略
            
                 
                @Resource
                private ProductDao productDao;
            
             
                @DataProvider
                public Collection<Product> getProductsByCategoryId(Long parameter) {
                    return productDao.find("from Product where category.id=" + parameter);
                }
                ..省略
            }

            這個方法通過一個category的id獲取對應產品分類中的產品列表。那么其中的parameter參數怎么傳進來的呢,我們看一下Reference中的parameter屬性設定:"$${this.id}"這個雙$的表達式,我們之前在EL表達式介紹的時候說明過,這時使用時動態計算的一種表達式,每一次使用都會被重新計算。這種表達式是在瀏覽器端執行的,因此其中的this就比較容易理解了,就是指當前使用中的實體對象(Entity),在本例就是當前的Category。這個參數的整體含義就是獲取當前Category的id屬性作為Reference的parameter參數的值。在范例中,當每一次我們將當前的Category切換為別的Category的時候,當前Category對象都會發生變化,也就是說this所指的實體對象就會發生變化,由于采用的是動態表達式,這樣綁定產品列表的Grid在每一次Category發生變化的時候都會嘗試著訪問其中的products屬性,由于products是Reference類型,它就會激活其中的dataProvider機制獲取數據,并返回自身的parameter屬性,獲取parameter屬性的時候就會激活動態EL表達式的計算規則,得到當前Category的id,并作為DataProvider的參數發出獲取數據的請求。這樣我們之前在ProductInterceptor.java中的getProductsByCategoryId方法就能得到對應的categoryId的值了。 Reference這個對象是懶裝載的,它只是把這個關系告訴瀏覽器,只有在瀏覽器中我們試圖訪問Reference的時候才會觸發數據加載工作,并通過一系列的機制調用到DataProvider獲取相關的數據。通過這種處理機制,我們可以提高某些類型頁面的性能,并極大的降低網絡傳輸的數據量。實作主從維護界面

            以上內容是否對您有幫助:
            在線筆記
            App下載
            App下載

            掃描二維碼

            下載編程獅App

            公眾號
            微信公眾號

            編程獅公眾號

            中文字幕人成乱码熟女|强行入侵女人a片|亚洲日本成本人观看|天天操天天操