表結構設計,實體類與DAO類設計
- 0項目來源與閱讀建議
-
- 項目來源
- 價格
- 閱讀建議
- 1 表設計與實體類概覽
-
- 表設計
- 實體類設計(第一版)
- 實體類設計(第二版)
- 實體類設計(第三版)
- 2 DAO類分析
-
- 2.1 CategoryDAO
-
- CRUD
- 分頁相關
- id與對象的映射關系
- 下面相同或類似的方法不再分析。
- 2.2 PropertyDAO
-
- CRUD
- list方法
- 2.3 ProductDAO
-
- fill方法
- search方法
- list方法
- 與其他實體類有關聯的方法
-
- setFirstProductImage
- setSaleAndReviewNumber
- 2.4 ProductImageDAO
- 2.5 PropertyValueDAO
- 2.6 OrderItemDAO
-
- Order和OrderItem中的關系
- 如何區分是訂單還是在購物車中
- 查詢方法
- 其他DAO
- 3.本章小結
0項目來源與閱讀建議
項目來源
http://how2j.cn/k/tmall-j2ee/tmall-j2ee-894/894.html?p=66748
注冊的時候用這個連結,謝謝啦!
價格
92 * 0.8
閱讀建議
- 本教程與站長的官方教程互為補充,并不是取而代之,也做不到取而代之。站長是以老師的身份告訴你怎麼寫,我是以課代表的身份告訴你,我是怎麼學的。
- 本教程應當在你做完站長相對應部分的内容之後,再來看。也就是說,在看本教程之前,你應當對站長對應章節的内容有了一定的了解和基礎,不然某些詞語你可能聽不懂。
- 我在How2j的ID名叫HuangTY,我喜歡分享大家存在的問題,是以也希望大家能夠在評論區多多留言,共同進步。
1 表設計與實體類概覽
表設計
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSMFRUT5lFRNNzaE5keVpWT0M2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1kjN5ATNyEjM4IjMxgTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
實體類設計(第一版)
表設計中關系均為1對多關系。1對多關系,在表設計中通常采用主從表設計,即主表的主鍵作為從表的外鍵存在。
這個關系如何在實體類的設計中展現出來呢?即,在表設計中處于從表地位的對應實體類中必須包括處于主表地位所對應的主體類。
用大白話中就是說,在“一對多”的表設計情況下,表示“多”的實體類中必須存有表示“一”的實體類對象,以此來代表外鍵。
那問題來了,我需不需要在代表“一”的實體類中增加List<“多”>呢?可以放,也可以不放。我們看站長是怎麼做的——
實體類設計(第二版)
由上圖可見,一對多關系中,在“一”的實體類中是否要存放針對“多”的List,看實際需求。比如說,為什麼Category中要放List<Product>呢?因為在前台顯示的時候,傳遞一個域對象為Category c,然後在jsp中擷取到了c,c中又有list,這樣在前台就可以直接展示c中的全部商品了,豈不美哉?
設想一下,計入Categroy中沒有List,那麼就要傳遞兩個域對象。
實體類設計(第三版)
這一版增加了兩個業務邏輯需要使用的函數。
函數1:getStatusDesc
用處:在天貓上下的訂單會存在不同的狀态,比如待付款,待發貨,待收貨等,這個函數就是維護了這樣一個狀态。(其實本質上是把枚舉轉化成中文字元串而已)
函數2:getAnnoymousName()
用處:評價商品的時候匿名。比如賬戶名叫 布朗尼的犀牛,顯示則為布***牛。
2 DAO類分析
2.1 CategoryDAO
CRUD
1.add()
較高版本的mysql采用站長的代碼可能存在一些問題,如下:
ResultSet rs = ps.getGeneratedKeys();
這句代碼Tomcat伺服器的log中可能出現報錯,大概意思是如果你想要擷取自動生成的主鍵,就需要在執行sql的時候指定,是以執行語句應該改為:
做的改動是加入了Statement.RETURN_GENERATED_KEYS。
除此之外,以上是java7的新特性,叫做try-with-resource。大概意思就是說,在try語句裡面調用的資源,将會在退出和異常的時候自動關閉。
2.update()
3.delete()
4.list()
這個就是查詢。這個代碼的傳入參數分别是,分頁起始值,每頁數量,對應的sql語句是:
就是從第一個?開始查詢,查詢數量為第二個?。
這個函數有傳回值,傳回的是查詢到的對象集合。查詢到的這個集合就可以放入域對象中,然後在jsp中顯示出來。
分頁相關
getTotal()
這個了解起來很簡單。每頁允許5個,那應該顯示多少頁呢?這是跟商品的總個數息息相關的。既然需要總數,那我們就用sql語句擷取一下就行了
id與對象的映射關系
Category get(id)
已知每個id均對應一個對象,通過get方法可以通過id得到Category的對應對象。
下面相同或類似的方法不再分析。
2.2 PropertyDAO
CRUD
這裡要強調的點是,Property表示Category的從表,是以以add()為例,展現從表在CURD時候與主表的關系。
注意參數是一個對象,由前文,該對象存在一個Category的成員變量,這個實體類具有get/set方法,是以可以擷取Category對象的id。這個id就是Product的外鍵(在表Property中稱為cid)。
//null為主鍵,會自動添加。第一個?為cid,即外鍵(category的id),第二個?為name。
String sql = "insert into Property values(null,?,?)";
...略...
ps.setInt(1, bean.getCategory().getId());
ps.setString(2, bean.getName());
通過bean.getCategory().getId()将category的id設定為product表中的cid。
list方法
看這個函數原型發現,要通過cid去查詢Property表,這是為什麼呢?
答案很顯而易見,見如下的問答:
Q:我想找到分類A的屬性,應該在那張表查詢?
A:既然是分類A的屬性,自然是屬性表,Property啊。
Q:通過什麼查詢呢?
A:見要查詢分類A的屬性,當然是查詢where cid = ?了啊。
是以,采用以下SQL語句。
2.3 ProductDAO
fill方法
public void fill(Category c)
public void fill(List<Category> cs)
可以看到這是一個重載的函數。下面那個是基于上面的,本質上隻是多了一個循環而已。
下面用大白話解釋一下這兩個函數
函數1:将分類c中的所有product放入其中。
函數2:有多個分類,cs={c1,c2,…cn}。對他們都處以函數1的操作。
具體來說——
Category c,這個對象中的List<Product>這個成員變量,需要ProducDAO調用fill後,才完成指派操作。
search方法
本質上就是模糊查詢,你看sql語句就可以知道。
沒學過sql語句的同學出門左轉。學過sql語句的同學看到like就知道,這就是模糊比對。
list方法
public List<Product> list(int start, int count){
String sql = "select * from Product limit ?,? ";
}
public List<Product> list(int cid, int start, int count){
String sql = "select * from Product where cid = ? order by id desc limit ?,? ";
}
上面兩個list方法非常有趣,一個沒有cid,一個有cid。我建議你再對照看一下PropertyDAO的list方法,然後就有了以下的問題:
Q:為什麼這裡有兩個list方法?
A:想一下應用場景,你會在什麼時候想要查詢商品呢?情況一,已知分類查詢商品,這裡就用到了函數2。情況二,我就想看所有的商品,那就用函數1.
Q:那為什麼PropertyDAO裡面不存在函數1呢?
A:因為從業務邏輯而言,脫離了分類的屬性是不存在的。也就是說,不存在一個業務場景,需要你檢視所有的屬性。業務場景僅為,檢視一個分類下的全部屬性。
與其他實體類有關聯的方法
setFirstProductImage
public void setFirstProductImage(Product p) {
List<ProductImage> pis= new ProductImageDAO().list(p, ProductImageDAO.type_single);
if(!pis.isEmpty())
p.setFirstProductImage(pis.get(0));
}
可以猜測出,ProductImageDao的list方法是根據p的id查詢出下面的所有标志位為“type_single”的圖檔,然後放入list集合中。其本質思想,和PropertyDAO的list(cid,start,count)沒有任何差別。
這個方法的作用就是把這個産品下關聯的所有圖檔中的第一張作為産品圖。
setSaleAndReviewNumber
public void setSaleAndReviewNumber(Product p)
public void setSaleAndReviewNumber(List<Product> products)
這兩個函數的關系,和fill的那兩個函數的關系是一樣的。
翻譯成大白話是:
将p的售賣數量和評論數量放入其中。
因為Product對象生成的時候,一般隻給了幾個基本屬性,至于List<>,sale和count都是用到的時候再擷取的,也是為了減少記憶體占用吧。
2.4 ProductImageDAO
産品圖檔之于産品 == 屬性之于分類
屬性和分類的關系叫做“組合”關系,這是一種強擁有關系,即産品必須有圖檔,且産品沒了圖檔也沒了,他們是同生共死的關系。産品圖檔和産品也一樣,一旦産品消失,那産品對應的圖檔也必須消失。
是以,我們可以對照比較Category與Property的關系,看一下Product和ProductImage的關系。
PropertyDAO:通過cid擷取與cid關聯的所有屬性
ProductImageDAO:通過産品對象p來獲得對應産品下的所有圖檔。
public List<ProductImage> list(Product p, String type)
public List<ProductImage> list(Product p, String type, int start, int count)
差別:為什麼PropertyDAO中是cid,ProductImageDAO中用的是對象p呢?
原因:站長在這裡寫的不統一,因為是寫cid也好,寫對象也好,本質上id和對象是對應關系。比如這裡不傳Product p而是傳入int pid(product對象的id),那你隻要Product.getId(pid)即可獲得對象。是以兩種寫法本質上等價,都無所謂的。
2.5 PropertyValueDAO
我們知道PropertyValue與Product和Property均是多對一關系,是以實體類中需要有Product對象和Property對象。
基本關系是:一個産品的一個屬性,對于一個屬性值。
下面的介紹都是圍繞這個基本關系來的:
get方法就是利用這個基本關系獲得的屬性值。
list可以查詢某個商品下的全部屬性值,當然也能獲得property,是以在Property表中任意給出一個外鍵或主鍵查詢,都能得出與之比對的全部資訊。
public void init(Product p) {
//查詢分類的全部屬性(分類有屬性,商品屬于分類,商品有這個分類的屬性及屬性值)
List<Property> pts= new PropertyDAO().list(p.getCategory().getId());
for (Property pt: pts) {
//比對标準:商品id和屬性id均相同的
//即找到商品p的對應屬性的屬性值
PropertyValue pv = get(pt.getId(),p.getId());
//如果這個屬性值為空,那麼就建立一個屬性值
if(null==pv){
pv = new PropertyValue();
pv.setProduct(p);
pv.setProperty(pt);
this.add(pv);
}
}
}
init方法是比較有趣的,邏輯分析如下:
對于任意的商品p,都有n個屬性,這n個屬性對于n個屬性值,每個屬性值都稱作pv好了。
對于屬性值而言,具有Product和Property兩個對象。是以,對于pv而言,它需要通過某種手段來set自己的Product與Property。何種手動?就是Init。先通過PropertyDAO.list(cid)獲得cid分類下的所有屬性。商品P有n個屬性,屬性的集合叫做pts,集合中的每個元素(即屬性)為pt,一個p和一個pts(基本關系)決定一個propertyvalue,是以pv就可以set他的property和product了。
2.6 OrderItemDAO
Order和OrderItem中的關系
要弄清楚Order和OrderItem中的關系。Order表訂單,比如我下了一筆訂單叫“1号訂單”,這個訂單裡面有多個訂單項,每個訂單項大緻表示為[産品名,數量,…]資訊。
Order看成一個集合的話,其中的元素就是OrderItem。
但是聽清楚了,并不是所有OrderItem都在訂單中!因為OrderItem需要下單後,才會成為訂單的一部分。
坦白說一開始我就沒了解這個,後面慢慢做業務的時候才了解。看一下的QA:
Q:我想加入1個産品A進入購物車。(業務邏輯上的需求)
A:那就應該把産品(對象),數量等屬性封裝到一個類裡。
Q:這個類不就是Order類麼?
A:并不是,因為Order類已經代表成為了訂單,而你加入購物車說明還沒有下單啊。
Q:那放入什麼類裡面?
A:我建立了一個類叫做OrderItem。
Q:那我懂了,是不是說,我加入購物車中的商品都是加入OrderItem類,而我直接先下單的加入Order類啊?換句話說,OrderItem是購物車類,Order類是訂單類?
A:當然不是!就算你直接下訂單,也是先建立一個OrderItem對象,然後将這個OrderItem指向Order(因為OrderItem實體類中存在成員變量Order)。
等到後面做業務邏輯的時候再去體味一下這些話。反正要記住,訂單一定是基于訂單項,而訂單項本身又可以做一些與訂單無關的操作。
如何區分是訂單還是在購物車中
if(null==bean.getOrder())
ps.setInt(2, -1);
else
ps.setInt(2, bean.getOrder().getId());
在add中,如果這個訂單項是剛剛建立的,那麼就預設oid(代表Order的id)為-1,如果在後續操作中,這個OrderItem屬于了某個Order,那麼就把oid轉化為那個Order的的id。
查詢方法
有兩種查詢方法,
- 按照uesr查詢,即查詢這個使用者的訂單項。什麼叫使用者的訂單項呢?就是說這個使用者的購物車内容。
- 按照訂單查詢,即查詢訂單中包含的訂單項内容
public List<OrderItem> listByUser(int uid, int start, int count)
String sql = "select * from OrderItem where uid = ? and oid=-1 order by id desc limit ?,? ";
public List<OrderItem> listByOrder(int oid, int start, int count)
String sql = "select * from OrderItem where oid = ? order by id desc limit ?,? ";
其他DAO
自己看吧,沒什麼特别好說的。一定要結合表結構設計的那張圖來看哦。
3.本章小結
本章主要是和資料庫相關的。
竊以為,J2EE這個項目給我最大的收獲在于表設計,DAO,Filter+Servlet設計模式方面。至于具體業務代碼,我認為腦子夠用的人,總是能自己想出來的,但是這些設計方面的東西,不學還真是不知道呢。
預告:
下一章将講解Filter與Servlet的設計模式,歡迎大家多多留言評論,共同進步。