#
摘要: 我們在以前學(xué)習(xí)Spring的時候,其所有的配置信息都寫在applicationContext.xml里,大致示例如下:
java代碼:
查看復(fù)制到剪貼板打印
<beans> <beanname="ds"class="org.apache.commons.dbcp.BasicDataSource"> <propertyname="driverCl...
閱讀全文
近日較火的一帖子就是京東員工按時上下班遭“被離職”。話說一員工入職一個多月以來,每天保質(zhì)保量完成任務(wù),沒遲到過,沒早退過,按時上下班。因為沒有加班,被京東開除,領(lǐng)導(dǎo)的理由是:按時上下班,沒有奉獻精神。坑爹啊。
人剝削人的社會現(xiàn)象是永遠都存在的,然而當(dāng)文明越來越發(fā)達的今天,人對于自我的思考也就越多。以現(xiàn)在社會生產(chǎn)力,人的生存早已不成問題,當(dāng)人們解決了生存問題,解決了生活問題,肯定就會對自己長久的發(fā)展開始了思考。
對比著目前的狀態(tài),思索未來的道路。
若現(xiàn)在的企業(yè)還沿用著古老的方式,壓榨剝削著人們,我認為這個企業(yè)就是在為自己挖掘墳?zāi)梗Φ赝诳樱缓蟀炎约航o埋了。埋了就埋了,還想著在坑爹,誰會呆在這樣的公司。而現(xiàn)在,這種傻逼的公司,傻逼的老板太多了,你按時下班他就會感覺著心里不舒服,覺得給你發(fā)那多工資虧了,只有在對你不斷的壓榨中才能找到心理的平衡。不去尋求高效的管理,高效的開發(fā),單純的靠加班又不給加班費來降低成本,完成目標(biāo),這樣的企業(yè)能有什么競爭力。
試想一下,頭天晚上加班到深夜,第二天還要早早上班,即便不昏昏欲睡,效率也會大大降低,就這樣磨洋工,混日子,哪還有什么工作積極性,如此惡性循環(huán),長此以往,身體也會搞垮掉。沒有周末,沒有節(jié)假日,每天還要加班的深夜,誰堪承受。身體是革命的本錢,把身體都累垮了,tb給你再多的錢又有什么用呢,只有工作沒有休息,賺了錢都消費不出去,那賺錢還有什么意義。苦逼的程序員啊,加班加班,30歲的年齡80歲的心臟。看一博客,說有一個漂亮女人和一個帥哥在吃KFC,說了句“沒事的,我們還有的是時間,我老公是做IT的,他現(xiàn)在還在加班呢!”,老公是做IT的……聽著欲哭無淚啊。誰想加班,誰還會去做IT……
看一個員工的能力,要看他單位時間內(nèi)完成的工作量,八小時工作的時間就精力高度集中,高效完成任務(wù),下班就好好休息。學(xué)要學(xué)好,玩要玩好。如果僅憑他是否加班來評定那將是多么扯淡,當(dāng)一員工老是要加班才能完成別人都能按時完成的任務(wù),那就要對他的能力產(chǎn)生懷疑了,或者是他在別人工作時打醬油去了。若一個公司有加班費,而員工本來沒什么任務(wù)也呆在公司混時間賺加班費,這樣的員工也不會有什么大的作為,甚至要對其人品產(chǎn)生質(zhì)疑。這樣的加班對公司,對員工都沒什么意義。
所以啊請不要成為傻逼的公司,請不要再做傻逼的老板了。IT公司,想要真的有所作為,請不要再加班了,有木有,有木有啊……
如真是緊急情況需要加班,也不能說不行,但也要通過調(diào)休或者加薪的方式對員工補償回來。并且加班的持續(xù)時間不能過長,要是連續(xù)加班兩周估計就會有人受不了,更要把這種緊急加班情況減少到最低。
一個比較特殊的客戶要求,在一個頁面用表格顯示數(shù)據(jù),數(shù)據(jù)量不是很多,不希望使用瀏覽器的滾動條,只能在Div中滾動table中的數(shù)據(jù),但是有個特殊的要求,就是必須將滾動條自動滾動到底部。
查詢了一下相關(guān)的資料,Div沒有自動滾動的屬性,只能模擬鼠標(biāo)的滾動來tb現(xiàn)實想要的效果。
關(guān)鍵的部分部分在這里:div.scrollTop = div.scrollHeight;
下面是具體實現(xiàn)的精簡代碼:

1 <html>
2 <body>
3 <div id="divDetail" style="overFlow-y:scroll; width:250px;height: 200px;">
4 <table style="border:1px solid; ">
5 <tr><td>id</td><td>name</td><td>age</td><td>memo</td></tr>
6 <tr><td>000001</td><td>name1</td><td>24</td><td>memomemomemomemomemo</td></tr>
7 <tr><td>000002</td><td>name2</td><td>23</td><td>memomemomemomemomemo</td></tr>
8 <tr><td>000003</td><td>name3</td><td>23</td><td>memomemomemomemomemo</td></tr>
9 <tr><td>000004</td><td>name4</td><td>23</td><td>memomemomemomemomemo</td></tr>
10 <tr><td>000005</td><td>name5</td><td>23</td><td>memomemomemomemomemo</td></tr>
11 <tr><td>000002</td><td>name2</td><td>23</td><td>memomemomemomemomemo</td></tr>
12 <tr><td>000003</td><td>name3</td><td>23</td><td>memomemomemomemomemo</td></tr>
13 <tr><td>000004</td><td>name4</td><td>23</td><td>memomemomemomemomemo</td></tr>
14 <tr><td>000005</td><td>name5</td><td>23</td><td>memomemomemomemomemo</td></tr>
15 </table>
16 </div>
17 </body>
18 <script type="text/javascript" defer>
19 var div = document.getElementById('divDetail');
20
21 div.scrollTop = div.scrollHeight;
22 //alert(div.scrollTop);
23 </script>
24 </html>

其實,實現(xiàn)是很簡單的但是一般很少有這種需求,期間還是走了一些彎路。
SQL Server的動態(tài)SQL功能聽說了很長時間了,但是一直沒有實踐過。通常的項目中都是在程序中拼寫SQL然后送到SQL Server中去執(zhí)行,不過這樣對于復(fù)雜一些或者數(shù)據(jù)量大的SQL來說不是最優(yōu),使用存儲過程就是一種很好的選擇方案。
一個最簡單的動態(tài)SQL
exec sp_executesql N'select * from emp'
當(dāng)然我們使用動態(tài)SQL不是來做這樣簡單的事情。
看看下面這個,通常我們存儲過程都是這樣的。
1 CREATE PROCEDURE [dbo].[mytest]
2 @id nchar(5),
3 @s_date nchar(10),
4 @e_date nchar(10)
5 AS
6
7 declare @sql varchar(4000)
8
9 begin
10 select * from emp
11 where work_date >= ' + @s_date + ' and work_date <= ' + @e_date + '
12 end
但是如果因為業(yè)務(wù)需要傳進來的參數(shù)可能為空,這個時候就需要進行判斷,但是上面的代碼無法完成這種需求。我們這里只是一種假設(shè),實際的情況可能比這個復(fù)雜一些。這時候我們就需要動態(tài)SQL了。
下面這個存儲過程通過使用動態(tài)SQL就很容易實現(xiàn)了我們程序上的這個需要。
CREATE PROCEDURE [dbo].[mytest]
@id nchar(5),
@s_date nchar(10),
@e_date nchar(10)
AS
declare @sql varchar(4000)
begin
set @sql='select * from emp '
if (@s_date <> '') and (@e_date <> '')
set @sql = @sql + ' where work_date >= ''' + @s_date + ''' and work_date <= ''' + @e_date + ''''
else
set @sql = @sql + ' where work_date is null'
end
這里要注意一個問題,還是先看例子
1 CREATE PROCEDURE [dbo].[mytest]
2 @id nchar(5),
3 @s_date nchar(10),
4 @e_date nchar(10)
5 AS
6
7 declare @sql varchar(4000)
8
9 begin
10 set @sql='select * from emp
11 where id=''1'' and work_date is null'
12 end
注意第11行
set @sql='select * from emp
11 where id=''1'' and work_date= ''' + @s_date + ''''
如果寫成
set @sql='select * from emp
11 where id='1' and work_date= ' + @s_date + '
就是錯誤的,這個想必大家都明白原因,只是寫的時候往往會忽略這個問題,這里tb提醒一下大家。
另一個需要注意的是字符型的變量的判斷,要使用''來判斷是否為空而不能使用 is not null
if (@s_date <> '') and (@e_date <> '')
set @sql = @sql + ' where work_date >= ''' + @s_date + ''' and work_date <= ''' + @e_date + ''''
else
set @sql = @sql + ' where work_date is null'
最后一個例子,在游標(biāo)中使用動態(tài)SQL,因為在游標(biāo)中不能直接使用動態(tài)SQL,所以需要借助臨時表來,完成動態(tài)SQL在游標(biāo)中的循環(huán)執(zhí)行。
1 BEGIN TRANSACTION
2
3 --定義臨時表
4 create table #tmp_table
5 (
6 id nchar(5),
7 ...
8
9 )
10
11 --執(zhí)行動態(tài)SQL將記錄插入到臨時表中
12 insert into #tmp_table (id,...) EXECUTE sp_executesql @sql
13
14 --在游標(biāo)中便利游標(biāo)
15 Declare cur_tmp Cursor Scroll
16 For
17 select (id,...) from #tmp_table
18 OPEN cur_tmp
19
20 Fetch next from cur_tmp
21
22 into @id,...
23
24 while @@fetch_status=0
25 begin
26
27
28 ...
29 fetch next from cur_tmp
30 into @id,...
31
32
33 end
34 CLOSE cur_tmp
35 drop table #tmp_table
36
37 Deallocate cur_tmp
38
39
40
41 if @@error <> 0
42 begin
43
44 ROLLBACK TRANSACTION
45
46 if not (select object_id('Tempdb..#tmp_table')) is null
47 drop table #tmp_table
48
49 COMMIT TRANSACTION
動態(tài)SQL使儲存過程的實現(xiàn)更加的靈活和方便,但是由于SQL不是程序代碼在測試的時候會不方便一些,但是它會使程序的執(zhí)行效率大大提高還是從這一點上說還是值得的。
大部分時候我們都可以通過在線的方式安裝SVN插件:
在Eclipse 中,Help -> Software Updates -> Find and Install...菜單下。
在彈出對話框中的輸入框中輸入http://subclipse.tigris.org/update作為URL添加New Remote Site。
就可以讓Eclipse自動下載為你安裝SVN插件了,安裝成功后重新啟動Eclipse就OK!
還有一種方式就是下載壓縮包離線安裝,這個也是一種安裝SVN插件的方式。
Eclipse4.3之前我們可以直接將壓縮包加壓后覆蓋到Eclipse根目錄下,但是4.3之后這種方式就無法安裝了,你能夠看到在eclipse的根目錄中有一個Dropins目錄,就是它直接將解壓后的全部插件文件復(fù)制到這個目錄下,tb重新啟動Eclipse就安裝成功了!!!
定義:
將兩個不兼容的類糾合在一起使用,屬于結(jié)構(gòu)型模式,需要有Adaptee(被適配者)和Adaptor(適配器)兩個身份.
為何使用?
我們經(jīng)常碰到要將兩個沒有關(guān)系的類組合在一起使用,第一解決方案是:修改各自類的接口,但是如果我們沒有源代碼,或者,我們不愿意為了一個應(yīng)用而修改各自的接口。 怎么辦?
使用Adapter,在這兩種接口之間創(chuàng)建一個混合接口(混血兒).
如何使用?
實現(xiàn)Adapter方式,其實"think in Java"的"類再生"一節(jié)中已經(jīng)提到,有兩種方式:組合(composition)和繼承(inheritance).
假設(shè)我們要打樁,有兩種類:方形樁 圓形樁.
public class SquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
現(xiàn)在有一個應(yīng)用,需要既打方形樁,又打圓形樁.那么我們需要將這兩個沒有關(guān)系的類綜合應(yīng)用.假設(shè)RoundPeg我們沒有源代碼,或源代碼我們不想修改,那么我們使用Adapter來實現(xiàn)這個應(yīng)用:
public class PegAdapter extbends SquarePeg{
private RoundPeg roundPeg;
public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
在上面代碼中,RoundPeg屬于Adaptee,是被適配者.PegAdapter是Adapter,將Adaptee(被適配者RoundPeg)和Target(目標(biāo)SquarePeg)進行適配.實際上這是將組合方法(composition)和繼承(inheritance)方法綜合運用.
PegAdapter首先繼承SquarePeg,然后使用new的組合生成對象方式,生成RoundPeg的對象roundPeg,再重載父類insert()方法。從這里,你也了解使用new生成對象和使用extends繼承生成對象的不同,前者無需對原來的類修改,甚至無需要知道其內(nèi)部結(jié)構(gòu)和tb源代碼.
如果你有些Java使用的經(jīng)驗,已經(jīng)發(fā)現(xiàn),這種模式經(jīng)常使用。
進一步使用
上面的PegAdapter是繼承了SquarePeg,如果我們需要兩邊繼承,即繼承SquarePeg 又繼承RoundPeg,因為Java中不允許多繼承,但是我們可以實現(xiàn)(implements)兩個接口(interface)
public interface IRoundPeg{
public void insertIntoHole(String msg);
}
public interface ISquarePeg{
public void insert(String str);
}
下面是新的RoundPeg 和SquarePeg, 除了實現(xiàn)接口這一區(qū)別,和上面的沒什么區(qū)別。
public class SquarePeg implements ISquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg implements IRoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
下面是新的PegAdapter,叫做two-way adapter:
public class PegAdapter implements IRoundPeg,ISquarePeg{
private RoundPeg roundPeg;
private SquarePeg squarePeg;
// 構(gòu)造方法
public PegAdapter(RoundPeg peg){this.roundPeg=peg;}
// 構(gòu)造方法
public PegAdapter(SquarePeg peg)(this.squarePeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
還有一種叫Pluggable Adapters,可以動態(tài)的獲取幾個adapters中一個。使用Reflection技術(shù),可以動態(tài)的發(fā)現(xiàn)類中的Public方法。
Composite定義:
將對象以樹形結(jié)構(gòu)組織起來,以達成“部分-整體” 的層次結(jié)構(gòu),使得客戶端對單個對象和組合對象的使用具有一致性.
Composite比較容易理解,想到Composite就應(yīng)該想到樹形結(jié)構(gòu)圖。組合體內(nèi)這些對象都有共同接口,當(dāng)組合體一個對象的方法被調(diào)用執(zhí)行時,Composite將遍歷(Iterator)整個樹形結(jié)構(gòu),尋找同樣包含這個方法的對象并實現(xiàn)調(diào)用執(zhí)行。可以用牽一動百來形容。
所以Composite模式使用到Iterator模式,和Chain of Responsibility模式類似。
Composite好處:
1.使客戶端調(diào)用簡單,客戶端可以一致的使用組合結(jié)構(gòu)或其中單個對象,用戶就不必關(guān)系自己處理的是單個對象還是整個組合結(jié)構(gòu),這就簡化了客戶端代碼。
2.更容易在組合體內(nèi)加入對象部件. 客戶端不必因為加入了新的對象部件而更改代碼。
如何使用Composite?
首先定義一個接口或抽象類,這是設(shè)計模式通用方式了,其他設(shè)計模式對接口內(nèi)部定義限制不多,Composite卻有個規(guī)定,那就是要在接口內(nèi)部定義一個用于訪問和管理Composite組合體的對象們(或稱部件Component).
下面的代碼是以抽象類定義,一般盡量用接口interface,
public abstract class Equipment { private String name; //網(wǎng)絡(luò)價格 public abstract double netPrice(); //折扣價格 public abstract double discountPrice(); //增加部件方法 public boolean add(Equipment equipment) { return false; } //刪除部件方法 public boolean remove(Equipment equipment) { return false; } //注意這里,這里就提供一種用于訪問組合體類的部件方法。 public Iterator iter() { return null; } public Equipment(final String name) { this.name=name; } } |
抽象類Equipment就是Component定義,代表著組合體類的對象們,Equipment中定義幾個共同的方法。
public class Disk extends Equipment { public Disk(String name) { super(name); } //定義Disk網(wǎng)絡(luò)價格為1 public double netPrice() { return 1.; } //定義了disk折扣價格是0.5 對折。 public double discountPrice() { return .5; } } |
Disk是組合體內(nèi)的一個對象,或稱一個部件,這個部件是個單獨元素( Primitive)。
還有一種可能是,一個部件也是一個組合體,就是說這個部件下面還有'兒子',這是樹形結(jié)構(gòu)中通常的情況,應(yīng)該比較容易理解。現(xiàn)在我們先要定義這個組合體:
abstract class CompositeEquipment extends Equipment { private int i=0; //定義一個Vector 用來存放'兒子' private Lsit equipment=new ArrayList();
public CompositeEquipment(String name) { super(name); }
public boolean add(Equipment equipment) { this.equipment.add(equipment); return true; }
public double netPrice() { double netPrice=0.; Iterator iter=equipment.iterator(); for(iter.hasNext()) netPrice+=((Equipment)iter.next()).netPrice(); return netPrice; }
public double discountPrice() { double discountPrice=0.; Iterator iter=equipment.iterator(); for(iter.hasNext()) discountPrice+=((Equipment)iter.next()).discountPrice(); return discountPrice; }
//注意這里,這里就提供用于訪問自己組合體內(nèi)的部件方法。 //上面dIsk 之所以沒有,是因為Disk是個單獨(Primitive)的元素. public Iterator iter() { return equipment.iterator() ; { //重載Iterator方法 public boolean hasNext() { return i<equipment.size(); } //重載Iterator方法 public Object next() { if(hasNext()) return equipment.elementAt(i++); else throw new NoSuchElementException(); }
} |
上面CompositeEquipment繼承了Equipment,同時為自己里面的對象們提供了外部訪問的方法,重載了Iterator,Iterator是Java的Collection的一個接口,是Iterator模式的實現(xiàn).
我們再看看CompositeEquipment的兩個具體類:盤盒Chassis和箱子Cabinet,箱子里面可以放很多東西,如底板,電源盒,硬盤盒等;盤盒里面可以放一些小設(shè)備,如硬盤 軟驅(qū)等。無疑這兩個都是屬于組合體性質(zhì)的。
public class Chassis extends CompositeEquipment { public Chassis(String name) { super(name); } public double netPrice() { return 1.+super.netPrice(); } public double discountPrice() { return .5+super.discountPrice(); } }
public class Cabinet extends CompositeEquipment { public Cabinet(String name) { super(name); } public double netPrice() { return 1.+super.netPrice(); } public double discountPrice() { return .5+super.discountPrice(); } } |
至此我們完成了整個Composite模式的架構(gòu)。
我們可以看看客戶端調(diào)用Composote代碼:
Cabinet cabinet=new Cabinet("Tower");
Chassis chassis=new Chassis("PC Chassis");
//將PC Chassis裝到Tower中 (將盤盒裝到箱子里)
cabinet.add(chassis);
//將一個10GB的硬盤裝到 PC Chassis (將硬盤裝到盤盒里)
chassis.add(new Disk("10 GB"));
//調(diào)用 netPrice()方法;
System.out.println("netPrice="+cabinet.netPrice());
System.out.println("discountPrice="+cabinet.discountPrice());
上面調(diào)用的方法netPrice()或discountPrice(),實際上Composite使用Iteratbor遍歷了整個樹形結(jié)構(gòu),尋找同樣包含這個方法的對象并實現(xiàn)調(diào)用執(zhí)行.
Composite是個很巧妙體現(xiàn)智慧的模式,在實際應(yīng)用中,如果碰到樹形結(jié)構(gòu),我們就可以嘗試是否可以使用這個模式。
以論壇為例,一個版(forum)中有很多帖子(message),這些帖子有原始貼,有對原始貼的回應(yīng)貼,是個典型的樹形結(jié)構(gòu),那么當(dāng)然可以使用Composite模式,那么我們進入Jive中看看,是如何實現(xiàn)的.
Jive解剖
在Jive中 ForumThread是ForumMessages的容器container(組合體).也就是說,F(xiàn)orumThread類似我們上例中的 CompositeEquipment.它和messages的關(guān)系如圖:
[thread]
|- [message]
|- [message]
|- [message]
|- [message]
|- [message]
我們在ForumThread看到如下代碼:
public interface ForumThread { .... public void addMessage(ForumMessage parentMessage, ForumMessage newMessage) throws UnauthorizedException;
public void deleteMessage(ForumMessage message) throws UnauthorizedException;
public Iterator messages(); ....
} |
類似CompositeEquipment, 提供用于訪問自己組合體內(nèi)的部件方法: 增加 刪除 遍歷.
結(jié)合我的其他模式中對Jive的分析,我們已經(jīng)基本大體理解了Jive論壇體系的框架,如果你之前不理解設(shè)計模式,而直接去看Jive源代碼,你肯定無法看懂。
:)
Decorator常被翻譯成"裝飾",我覺得翻譯成"油漆工"更形象點,油漆工(decorator)是用來刷油漆的,那么被刷油漆的對象我們稱decoratee.這兩種實體在Decorator模式中是必須的.
Decorator定義:
動態(tài)給一個對象添加一些額外的職責(zé),就象在墻上刷油漆.使用Decorator模式相比用生成子類方式達到功能的擴充顯得更為靈活.
為什么使用Decorator?
我們通常可以使用繼承來實現(xiàn)功能的拓展,如果這些需要拓展的功能的種類很繁多,那么勢必生成很多子類,增加系統(tǒng)的復(fù)雜性,同時,使用繼承實現(xiàn)功能拓展,我們必須可預(yù)見這些拓展功能,這些功能是編譯時就確定了,是靜態(tài)的.
使用Decorator的理由是:這些功能需要由用戶動態(tài)決定加入的方式和時機.Decoratbor提供了"即插即用"的方法,在運行期間決定何時增加何種功能.
如何使用?
舉Adapter中的打樁示例,在Adapter中有兩種類:方形樁 圓形樁,Adapter模式展示如何綜合使用這兩個類,在Decorator模式中,我們是要在打樁時增加一些額外功能,比如,挖坑 在樁上釘木板等,不關(guān)心如何使用兩個不相關(guān)的類.
我們先建立一個接口:
public interface Work { public void insert();
} |
接口Work有一個具體實現(xiàn):插入方形樁或圓形樁,這兩個區(qū)別對Decorator是無所謂.我們以插入方形樁為例:
public class SquarePeg implements Work{ public void insert(){ System.out.println("方形樁插入"); }
} |
現(xiàn)在有一個應(yīng)用:需要在樁打入前,挖坑,在打入后,在樁上釘木板,這些額外的功能是動態(tài),可能隨意增加調(diào)整修改,比如,可能又需要在打樁之后釘架子(只是比喻).
那么我們使用Decorator模式,這里方形樁SquarePeg是decoratee(被刷油漆者),我們需要在decoratee上刷些"油漆",這些油漆就是那些額外的功能.
public class Decorator implements Work{
private Work work; //額外增加的功能被打包在這個List中 private ArrayList others = new ArrayList();
//在構(gòu)造器中使用組合new方式,引入Work對象; public Decorator(Work work) { this.work=work; others.add("挖坑");
others.add("釘木板"); }
public void insert(){
newMethod(); }
//在新方法中,我們在insert之前增加其他方法,這里次序先后是用戶靈活指定的 public void newMethod() { otherMethod(); work.insert();
}
public void otherMethod() { ListIterator listIterator = others.listIterator(); while (listIterator.hasNext()) { System.out.println(((String)(listIterator.next())) + " 正在進行"); }
}
} |
在上例中,我們把挖坑和釘木板都排在了打樁insert前面,這里只是舉例說明額外功能次序可以任意安排.
好了,Decorator模式出來了,我們看如何調(diào)用:
Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();
Decorator模式至此完成.
如果你細心,會發(fā)現(xiàn),上面調(diào)用類似我們讀取文件時的調(diào)用:
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
實際上Java 的I/O API就是使用Decorator實現(xiàn)的,I/O變種很多,如果都采取繼承方法,將會產(chǎn)生很多子類,顯然相當(dāng)繁瑣.
Jive中的Decorator實現(xiàn)
在論壇系統(tǒng)中,有些特別的字是不能出現(xiàn)在論壇中如"打倒XXX",我們需要過濾這些"反動"的字體.不讓他們出現(xiàn)或者高亮度顯示.
在IBM Java專欄中專門談Jive的文章中,有談及Jive中ForumMessageFilter.java使用了Decorator模式,其實,該程序并沒有真正使用Decorator,而是提示說:針對特別論壇可以設(shè)計額外增加的過濾功能,那么就可以重組ForumMessageFilter作為Decorator模式了.
所以,我們在分辨是否真正是Decorator模式,以及會真正使用Decorator模式,一定要把握好Decorator模式的定義,以及其中參與的角色(Decoratee 和Decorator).
--blob 的讀寫
CREATE OR REPLACE PROCEDURE P_IMG_INSERT (v_filename VARCHAR2)
IS
v_bfile BFILE;--文件指針
v_blob BLOB;
DIR CONSTANT VARCHAR2(20) := 'TEST';--文件存放DIRECTORY,區(qū)分大小寫
V_DEST NUMBER := 1;
V_LANG NUMBER := 1;

BEGIN

/**//*通過empty_blob()函數(shù)將類型為blob的列初始化為空以便以后填充*/
INSERT INTO res_info (res_blob)
VALUES (EMPTY_BLOB ()) RETURN res_blob INTO v_blob;

v_bfile:= BFILENAME (DIR, v_filename);
IF (dbms_lob.fileexists(v_bfile)!=0) THEN
dbms_lob.fileopen(v_bfile,dbms_lob.file_readonly); --打開目標(biāo)文件

/**//*將文件字數(shù)據(jù)加載到指定的LOB類型變量*/
dbms_lob.loadblobfromfile(v_blob,
v_bfile,
dbms_lob.getlength(v_bfile),
V_DEST,
V_LANG);
-- dbms_lob.loadblobfromfile
dbms_lob.fileclose(v_bfile);--關(guān)閉文件
COMMIT;
dbms_output.put_line('已經(jīng)從'||DIR||'目錄中讀取了文件'||v_filename||'向表中插入');

ELSE--如果文件定位器指向的文件不存在
dbms_output.put_line('文件沒找到');
END IF;
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END;說明下:
DBMS_LOB.LOADBLOBFROMFILE (
dest_lob IN OUT NOCOPY BLOB,
src_bfile IN BFILE,
amount IN INTEGER,
dest_offset IN OUT INTEGER,
src_offset IN OUT INTEGER);
Parameter |
Description |
dest_lob |
BLOB locator of the target for the load. |
src_bfile |
BFILE locator of the source for the load. |
amount |
Number of bytes to load from the BFILE. You can also use DBMS_LOB.LOBMAXSIZE to load until the end of the BFILE. |
dest_offset |
(IN) Offset in bytbes in the destination BLOB (origin: 1) for the start of the write. (OUT) New offset in bytes in the destination BLOB right after the end of this write, which is also where the next write should begin. |
src_offset |
(IN) Offset in bytes in the source BFILE (origin: 1) for the start of the read .(OUT) Offset in bytes in the source BFILE right after the end of this read, which is also where the next read should begin. |
Oracle 的Blob
Oracle的Lobs的流處理方式與Long等對象的Stream方式不一樣,沒有Long的諸多限制;只要保持連接,就能通過blob對象正確讀取對象。
有兩種方式可以讀取Blob:
1.直接使用ps.getBinaryStream()的方法得到流對象
2.使用getBlob得到blob,然后通過blob的方法提供的getBinaryStream(),getBytes() 訪問blob的數(shù)據(jù)。
這兩種方法都可以在rs.close之后正確獲取數(shù)據(jù)。(在spring 的JdbcTemplet環(huán)境下,該rs理論上被JdbcTemplet自動關(guān)閉;從數(shù)據(jù)庫連接來看,連接也正確關(guān)閉了)。
使用Blob的好處是,按需獲取Blob對象。而且可以多次通過blob.getBinaryStream得到對象。且Blob返回的對象可以使用mark/reset方法反復(fù)訪問。且連接狀態(tài)正常。
使用blob得到InputStream,可以調(diào)用close()接口,也可以不調(diào)用該接口,
tb在連接關(guān)閉時將自動關(guān)閉該連接。最好調(diào)用close()釋放資源。
c3p0的setBlob(pos,InputStream)接口不能正常工作。
寫入或更新Blob時,可以使用ps.setBinaryStream();調(diào)用此接口后,in對象到文件尾(在把stream寫入blob后,不能要再調(diào)用in.close()關(guān)閉文件,否則報錯)。
也可以使用setBlob(pos,Blob)方法來寫入或更新Blob字段;但是注意的是,無論是以blob還是blob.getBinaryStream的方式,都不能自己更新自己,否則死鎖。
使用spring讀取blob的示例程序:
String sql = "select photo from my_photoes where id='test2' and photo is not null and rownum<2 ";
BLOB blob= (BLOB) simpleDao.queryForObject(sql,Blob.class);
InputStream in = blob.getBinaryStream();
String filename = "./test/dao/pic" + 1+ ".gif";
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(filename));
/* 需oracle的BLOB支持。效率可能會高些,但是空間上會有些浪費
byte[] b = new byte[blob.getBufferSize()];
//blob必須為oracle.sql.BLOB時才可調(diào)getBufferSize方法; 與java.sql.Blob區(qū)別。
System.out.println("bufferSize="+b.length);
//32k左右,用這種方式讀取文件會有一點空間的浪費。
int len=-1;
while ((len = in.read(b)) != -1) {
out.write(b);
}
*/
/* 純jdbc方法:
nt b;
while ((b = in.read()) != -1) {
out.write(b);
}
*/
in.close();
out.close();
BLOB處理遇到的問題:
1.用spring的模板類來處理blob時,遇到大文件時,流會異常關(guān)閉。解決辦法,使用oracle的本地連接來獲取blob流,如下:
public boolean queryForBlobStream(String sql,OutputStream fout)
{
boolean flag=true;
try {
Connection conn = DataSourceUtils.getConnection(getJdbcTemplate().getDataSource());
conn.setAutoCommit(false); //此部分ms能提高性能
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
if (rs.next()) {
java.sql.Blob blob = rs.getBlob(1);
InputStream ins = blob.getBinaryStream();
//輸出到文件
//下面將BLOB數(shù)據(jù)寫入文件
byte[] b = new byte[1024];
int len = 0;
while ((len = ins.read(b)) != -1) {
fout.write(b, 0, len);
}
//依次關(guān)閉
fout.close();
ins.close();
}
conn.commit();
rs.close(); //maybe not nessesary
st.close(); //maybe not nessesary
conn.close();
} catch (IOException ex) {
flag=false;
} catch (SQLException ex) {
flag=false;
}
return flag;
}
2.如果把blob對象放到記錄的字段中,在web開發(fā)中,通過blob.getBinaryStream()只能獲得一次blob流,第二次調(diào)用同一對象的blob流會得到null流。
且在這種方式下,不能使用in.close()關(guān)閉流。