概述 在設(shè)計(jì)庫(kù)表時(shí),經(jīng)常會(huì)碰到用于保存"時(shí)間值"的字段,,如create_date,,begin_time,login_time等,,舉不勝舉,。針對(duì)這些類型的字段,在設(shè)置數(shù)據(jù)類型時(shí),有一個(gè)有趣的現(xiàn)象,,即其中一些人使用Date類型,,而另外一些人使用Char(8)/Char(14)類型。一般而言,,初學(xué)者,,在校學(xué)生,甚至老師一般都屬于前者,,他們一個(gè)鮮明的特征是對(duì)數(shù)據(jù)庫(kù)的理論掌握很好,,但普遍缺少實(shí)際項(xiàng)目的開發(fā)經(jīng)驗(yàn);而后者一般是那些有一定項(xiàng)目經(jīng)驗(yàn)的開發(fā)人員,。乍一看,,這些時(shí)間值字段,用Date類型應(yīng)該是合情合理,,天經(jīng)地義的,,為什么有一定項(xiàng)目經(jīng)驗(yàn)的人偏偏要這樣"棄暗投明",這樣"特立獨(dú)行"呢,? 這是典型的白貓黑貓問題,,理論化的東西很光鮮,但有時(shí)在實(shí)踐中就是不靈光,,而一些"旁門左道"的東西卻顯得更加方便易用,。本文將通過一個(gè)具體例子的不同開發(fā)過程,分析Char類型時(shí)間字段為什么在實(shí)踐中更受歡迎,??紤]到篇幅所限,我們僅對(duì)Date類型和Char(8)類型的時(shí)間值字段作比較分析,,對(duì)于Date類型和Char(14)類型的分析,,相信大家完全可以由此而及彼。 1,、比較的例子 我們?cè)O(shè)計(jì)了一個(gè)具體的實(shí)例,,對(duì)用Char類型和Date類型的日期進(jìn)行比較分析,使用的是Oracle數(shù)據(jù)庫(kù),,現(xiàn)對(duì)該實(shí)例進(jìn)行簡(jiǎn)單的描述。 假設(shè)有一個(gè)T_USER表,,有一個(gè)EXPIRE_DATE(過期日期)字段,,要求記錄年、月,、日的日期數(shù)據(jù),,對(duì)EXPIRE_DATE字段分別采用兩種實(shí)現(xiàn)方式:
圖 1 T_USER表 左邊的T_USER(1)使用CHAR(8)保存日期值,以yyyymmdd格式保存,,如20070606,,20070501,;而右邊的T_USER(2)使用Date數(shù)據(jù)類型,我們稱左邊的數(shù)據(jù)表設(shè)計(jì)為CHAR類型日期方案,,而右邊的設(shè)計(jì)為DATE類型日期方案,。 表中的數(shù)據(jù)當(dāng)然不會(huì)生而有之,我們假設(shè)從Web的表單上提交上來,,保存到表中,,當(dāng)然還要有查詢、統(tǒng)計(jì)等操作,,我們就通過這些常見的數(shù)據(jù)操作分析這兩個(gè)方案的不同,,通過這樣的分析,孰劣孰優(yōu),,相信我們就可以進(jìn)行很好的判斷了,。 2、從表單添加記錄的比較 Struts+Spring+Hibernate是目前Web項(xiàng)目中流行的框架,,在這個(gè)框架中,,Hibernate需要為T_USER生成一個(gè)User.java的PO,CHAR類型日期方案的User.expireDate為String類型,,而DATE類型日期方案的User.expireDate為java.sql.Date類型,,如圖 2所示:
圖 2 兩方案分別對(duì)應(yīng)的User.java PO 而對(duì)應(yīng)Struts的展現(xiàn)層,需要提供一個(gè)UserActionForm,,以獲取頁(yè)面表單的提交數(shù)據(jù),。不管采用哪種日期方案,UserActionForm.expireDate屬性類型均為String,,因?yàn)檫@樣一來,,可以直接從Struts的<html:text property="expireDate"/>獲取數(shù)據(jù),,另外也方便數(shù)據(jù)回顯到頁(yè)面中,;如果UserActionForm.expireDate采用java.sql.Date類型,,則<html:text property="expireDate"/>標(biāo)簽的數(shù)據(jù)將無法正確地填充UserActionForm.expireDate對(duì)象屬性中,。
圖 3 UserActionForm.java 表單提交上來的expireDate是帶時(shí)間格式的字符串,,如2006-06-06,,2001-10-12,,UserActionForm.expireDate簡(jiǎn)單地接受該值,,在UserAction中,,必須用UserActionForm的數(shù)據(jù)生成持久層所需的PO,,即User對(duì)象。兩種日期方案在數(shù)據(jù)的轉(zhuǎn)換邏輯的區(qū)別分別描述如下: ·CHAR類型日期 由于User.expireDate也是String類型,,因此,,僅需要將UserActionForm.expireDate屬性完全拷貝到User中,然后再將User.expireDate屬性的日期格式符"-"去除卻可,卻將2006-06-06轉(zhuǎn)換為20060606,,對(duì)應(yīng)操作邏輯的主要代碼如下: 1. User user = new User(); 2. //將userActionForm中的數(shù)據(jù)拷貝到user對(duì)象中 3. BeanUtils.copyProperties(user, userActionForm); 4. //將日期格式符去除,,得到數(shù)據(jù)庫(kù)存儲(chǔ)日期格式,如將2006-06-06轉(zhuǎn)換為20060606 5. user.setExpireDate(user.getExpireDate().replace("-","")); 6. … 7. //調(diào)用服務(wù)對(duì)象,,將user保存到T_USER中 8. userService.save(user); ·DATE類型日期 在DATE類型日期方案中,,由于PO User.expireDate屬性為java.sql.Date,和UserActionForm.expireDate 存在類型的不匹配,,因此需要通過一個(gè)轉(zhuǎn)換函數(shù),,將String日期轉(zhuǎn)換為java.sql.Date的日期。其主要代碼如下: 1. User user = new User(); 2. //由于expireDate不能直接進(jìn)行拷貝,,因此需要逐一拷貝屬性 3. BeanUtils.copyPropertie(user, userActionForm,"userId"); 4. BeanUtils.copyPropertie(user, userActionForm,"userName"); 5. //使用轉(zhuǎn)換函數(shù)str2Date()將String類型的時(shí)間轉(zhuǎn)換為java.sql.Date的時(shí)間 6. java.sql.Date expireDate = str2Date(userActionForm.getExpireDate()); 7. //設(shè)置expireDate屬性 8. user.setExpireDate(expireDate); 9. … 10. //調(diào)用服務(wù)對(duì)象,,將user保存到T_USER中 11. userService.save(user); 通過上面的比較,可以看出,,使用DATE時(shí)間方案比使用CHAR時(shí)間方案在添加數(shù)據(jù)的處理上要復(fù)雜一些,,表現(xiàn)在: 1) 由于屬性名相同而類型存在不可直接轉(zhuǎn)換的問題將導(dǎo)致無法進(jìn)行對(duì)象間屬性批量拷貝,即BeanUtils. copyProperties()批量屬性拷貝函數(shù)會(huì)拋出異常,,因此只能手工逐一進(jìn)行單個(gè)具體屬性的拷貝,,如果屬性個(gè)數(shù)很多,這一機(jī)械式的屬性拷貝代碼塊就要相應(yīng)增大,,不但使代碼顯示臃腫難看,,而且直接降低了代碼的可維護(hù)性,因?yàn)橐坏虮碜侄蚊淖?,就需要手工調(diào)整這段代碼,。 2) 需要提供一個(gè)將String日期串轉(zhuǎn)換為java.sql.Date的轉(zhuǎn)換函數(shù),將年,、月,、日時(shí)間域分別從字串中抽取出來,并轉(zhuǎn)換為int類型,,然后利用java.sql.Date(int year,int month,int date)構(gòu)造函數(shù)得到對(duì)應(yīng)的java.sql.Date對(duì)象,。 |
|