2008年5月6日 星期二

Unit Test Hibernate With Spring


摘要:探討 hibernate unit test 的四種可能作法,分析其利弊,結論是建議使用第三種作法 -- 實際的 db 搭配 transaction rollback。後半則分享 Hibernate 常見的三種 case: save、query及 lazyInitializationException 的測試方法和 建立 fixture 的技巧。
今天討論的是硬題目,Hibernate 的 Unit Test。UI 的 Unit Test 難度是最高的相信這個大家都同意。但 persistent 這個環結的測試也算是數一數二難的了,原因在於準備 fixture (註一)非常的困難,而測試結束後還要刪除所有 fixture,更是會讓人抓狂。所以有一些hibernate 測試的方法都是為了解決 fixture 的問題而想出來的。目前看到的四種作法是:

  1. 不接觸 db,直接測 hibernate 的語法和設定
  2. 測試時使用 in-memory db,快速且不用特別去刪用完的 fixture
  3. 使用實際的 db。在測試其間開啟單一 transaction,測試完成後藉 transaction rollback 清除 fixture。
  4. 使用實際的 db。fixture 的建立與刪除全部靠手寫的 SQL 來處理。

上面四種方法由上而下,integration 的程度越高,測試相對來講也越完整,但也代表測試越來越難寫。第一種不接觸 db 的做法是直接針對 hbm.xml 這個 xml 做 Unit Test,或是針對 HQL 的語法寫測試。這種做法不需準備 fixture,所以很快也很好寫,但缺點是沒什麼用!。hbm.xml、HQL 設定和語法寫對了,跟實際下法 query/insert... 得到結果根本是兩回事,尤其是 hibernate 有些地方是環環相扣的 (cascade/inverse=true... 之類),單一個 hbm.xml 寫對了不代表任何意義。

第二種是依賴 HSQLDB 之類的 in-memory db。即然是 in-memory,測試一跑完資料就可丟了,所以難的地方只剩另一半 -- 建立 fixture。聽起來是個不錯的作法,但有一個致命的缺點 -- 它不是針對 production db 作測試的;這種作法最常遇到的問題是 unit test 都跑的好好的,上線後出現一些難以捉摸的問題,追到後來才知道原來某某 db 在某某狀況下會怎麼怎麼樣。原本 db 間的相容性就不高了,在加上一層 abstract 的 jdbc driver 更是讓問題雪上加霜。很多不可預期的錯誤都是某版的 db + 某版的 jdbc driver 上才會出現 (對,我就是在說 oracle)。我們花了大把的時間建立 fixture、寫 persistent 的 unit test,最後還是沒事先幫我們捉到問題,這花的功夫可真是浪費了。

第三、四種都是使用實際的 db,所以可克服第二種作法的缺點。第三種作法藉 transaction rollback 的功能自動去除 fixture。換言之,跟第二種方法一樣,只需煩惱 fixture 的建立即可,唯一的缺點是無法測試跨 transaction 的功能。不過這是小問題,因為絕大多數的 DAO 實作都不控管 transaction,而是將它交由上一層的 service facade 來處理, DAO 本身通常在同一 transaction 裡操作。如果真有那種需求,可搭配第四種方法來測試。第四種是全手動的,所以可以支援所有狀況的測試,但也最費時費力,非到必要時不用。

好了,終於要回到正題了,我們來看看第三種的 hibernate unit test 該怎麼寫。首先你必需準備一個測試用的 db,而且最好能與實際上線的 db 版本一致,這個測試的 db 可以整組人共用,也可以每個人自己架一個自用。很多書上都建議最好每個人自備 test db,但我們 team 一直都是共用同一個,開發起來到也沒什麼問題,而且共用 db等於是一直處在整合的狀態,可以及早發現問題。

以下探討的架構是常見 Dao 配 hibernateTemplate 這樣的做法,spring 的細部設定就略過不提了,書上都有寫。測試的 TestCase 採用 spring-mock.jar 裡的 AbstractTransactionalSpringContextTests,下面是一個 UserDao 的 test case:

# public class UserDaoTest extends
# AbstractTransactionalSpringContextTests {
#
# //這個測試使用的 spring 設定檔,如果沒有特別的需求,直接拿 production 的來用即可。
# @Override
# protected String[] getConfigLocations() {
# return new String[] { "classpath:/applicationContext*.xml" };
# }
#
# // inject hibernateTemplate,方便做一些細部控制
# private HibernateTemplate hibernateTemplate;
#
# // class under test
# private UserDao dao ;
#
# // this will be auto-injected.
# public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
# this.hibernateTemplate = hibernateTemplate;
# }
#
# // this will be auto-injected.
# public void setUserDao(UserDao userDao) {
# this.dao = userDao;
# }
#
# //取代原來的 setUp(),這個 method 在 transaction 內。
# @Override
# protected void onSetUpInTransaction() throws Exception {
# }
#
# //取代原來的 tearDown(),這個 method 在 transaction 內。
# @Override
# protected void onTearDownInTransaction() throws Exception {
# }
#
# //真正的測試 method
# public void testSaveUser() throws Exception {
# //test here...
# }
# }

AbstractTransactionalSpringContextTests 繼承自 AbstractDependencyInjectionSpringContextTests,所以只要 test case 裡寫 setXXX(),它會自動從 spring container 裡 inject 同 type 的 bean。上面的例子我們有兩個 setter,一是 setHibernateTemplate(...),二是 setUserDao(...),它們都會自動 inject 供測試使用。getConfigLocations() 則是 config 檔的位置,起動 spring container 時會用到。

這個 test case 特別的地方在於,每次執行 testXXX() 時,spring 會自動開啟一個 transaction,直到 method 結束,到了 tearDown() 才會 rollback()。如果你需要寫 setUp()/tearDown(),可以用 onSetUpInTransaction() 和 onTearDownInTransaction() 替代,它們執行的時候也會在同一個 transaction 內。好,開始寫 testSaveUser():

public class UserDaoTest extends AbstractTransactionalSpringContextTests {

public void testSaveUser() {
User user = new User("aName");
dao.save(user);
assertNotNull(user.getId());
User found = hibernateTemplate.get(User.class, user.getId());
assertEquals(user.getId(), found.getId());
assertEquals(user.getName(), found.getName());
}
}

public class UserDao {
public void save(User user) {
hibernateTemplate.saveOrUpdate(user);
}
}

一開始先用 dao save 一個 user,然後 assert 它有取得的 database id,接下來利用 hibernateTemplate.get() 確認資料庫是否真的已儲存了剛才的那一筆。如果你的 hibernate 設定和資料庫都正確的話,你應該會拿到一個綠燈。測試完後,儲存的 user 資料會被 rollback,所以可以一直反覆執行這個測試。

初步的測試大概就是這樣寫... 還蠻好寫的,就寫些 java 程式就好了,也不用去管啥殺資料的索事了。但是很不幸地,上面的測試是無效的。首先我們來看看 dao.save(user) 做了什麼? 如果是一般使用 sequence 產生 id 的資料庫,你會看到像是這樣的 query:

select next value for seq_user from dual_seq_user

然後就沒了,沒有 insert 的 statement 啊!那接下的 hibernateTemplate.get(User.class, user.getId()) 呢?哇咧,仔細一看居然也沒有下任何的 sql。搞了半天上面的測試只執行了一行 sequence 相關的 sql,其他什麼都沒執行到!難怪會說這個測試是無效的。Why? Why? Why?

原因是 hibernate 的 first level cache 和 delay flush 兩個功能所引起的。Hibernate 執行 save()/saveOrUpdate() 時,當下只會去資料庫要個 id,還不會真的 insert (註二)。但經過這一個操作後, hibernate 便視 user 為 persistent,把該 instance 放在 1st level cache 裡。直到 flush() 才會真正的去執行 insert。一般來說 transaciton commit 時,就是 flush() 的時間點。可惜的是 AbstractTransactionalSpringContextTests 永遠不會做 commit 啊,它只會 rollback。因此遇到這個情況,我們得手動 flush():

public class UserDaoTest extends AbstractTransactionalSpringContextTests {

public void testSaveUser() {
User user = new User("aName");
dao.save(user);

//手動 flush,就會有 insert 了
hibernateTemplate.flush();

assertNotNull(user.getId());
//.. 其他略...
}
}

好,解決一半的問題,但還是沒看到 hibernateTemplate.get(User.class, user.getId()) 所執行的 sql 啊?原因是 hibernate 的 get() 會先去 1st level cache 找找看,如果找到同個 class,同個 id 的,它就直接回傳 cache 裡的物件給你,而不會下額外的 query。解決的辦法是手動清除 1st level cache:

public class UserDaoTest extends AbstractTransactionalSpringContextTests {

public void testSaveUser() {
User user = new User("aName");
dao.save(user);
//手動 flush,就會有 insert 了
hibernateTemplate.flush();
assertNotNull(user.getId());

//清除 first level cache:
hibernateTemplate.clear();

//這一行就真的會下 query 到 db,實際去撈撈看有沒有該筆資料。
User found = hibernateTemplate.get(User.class, user.getId());
assertEquals(user.getId(), found.getId());
assertEquals(user.getName(), found.getName());
}
}

呼~ 總算讓這個測試真的做到事了。雖然多了兩個討厭的步驟,但怎麼樣也比手動 tearDown 殺資料好多了。這點犧牲是小意思。由於 flush() 常常會配 clear(),那乾脆包成個 flushThenClearCache(),可以方便一點。

在寫 hibernate unit test 時,何時 flush, 何時 clear 是很重要的觀念和技巧,請小心使用。

上面第一個範例是小 case,接下來我們看一個 query 的例子:

public class UserDaoTest extends AbstractTransactionalSpringContextTests {

//共用的 fixture
List<User> users ;
@Override
protected void onSetUpInTransaction() throws Exception {

//手動 inject 到 UserOM, 如果嫌麻煩可以弄個 AbstractDaoTest 統一設定
UserOM.hibernateTemplate = hibernateTemplate;

//使用特別的 helper class 產生 fixture
users = UserOM.savedUsers("foo", "userX", "foo", "bar" );
}

public void testFindUsersByName_simple() {

//不要忘記清除 cache
flushThenClearCache();

//一般來講 query 的測試要從 0 筆開始測試。
List<User> notFound = dao.findUsersByName("not exist name");
assertEquals(0, notFound.size()):


//測試一個符合
List<User> barUsers = dao.findUsersByName("bar");
assertEquals(1, barUsers.size()):
//確認 id 就足夠了
assertEquals(users.get(0).getId(), barUsers.get(0).getId());
}

public void testFindUsersByName_complex() {
//不要忘記清除 cache
flushThenClearCache();

//測試要涵蓋到多個符合。
List<User> fooUsers = dao.findUsersByName("foo");

assertEquals(2, fooUsers.size()):
assertEquals(users.get(0).getId(), fooUsers.get(0).getId());
assertEquals(users.get(2).getId(), fooUsers.get(1).getId());
}

protected void flushThenClearCache() {
hibernateTemplate.flush();
hibernateTemplate.clear();
}
}

//專門產生假資料的 helper class (OM 意指 ObjectMother)
public class UserOM {

//manually inject in TestCase:
public static HibernateTemplate hibernateTemplate;

public static List<User> savedUsers(String...names) {
List<User> users = new ArrayList<User>();
for(String name : names) {
User user = new User(name);
hibernateTemplate.saveOrUpdate(user);
users.add(user);
}
return users;
}

//other methods
public static User savedUserWithGroup() {//略......}
}

//真正的 production 程式:
public class UserDao {
public List<User> findUsersByName(String name) {
String hql = " from User where name = ? " ;
return (List<User>) hibernateTemplate.find(hql, name);
}
}

這一段程式是查相同名字的使用者。真正的主程式才兩行,測試的程式卻快三十行,真是嘔死人。可是 db 就是這麼難測啊... 上面的 test case 我們寫了 simple 和 complex 兩個,simple 涵蓋 0~1 個的測試,complex 則是多個的。你想要再包含更多的 case 那也由你,不過個人通常就寫到這樣就停了,因為這樣的測試已經可以讓我對 produciton 程式有足夠的自信。而 UserOM 是很久之前提過的 ObjectMother pattern,我們會在一開始測試時給它 hibernateTemplate,讓它可以在測試程式中自由的 insert 假資料。這裡的 UserOM 仍然使用 hibernate 來做假資料,而不是用純 sql 或是 dbunit xml 的做法。個人是比較傾向這樣寫,因為用 hibernate 就只要操作 POJO 的屬性就好了,若有重覆的地方也可以做 refactoring (就 java 嘛) 久而久之 UserOM 裡 method 越來越多,重用性也越來越高,建立 fixture 就不會像一開始這麼累了。

另一個地方我們可以使力的地方是:

assertEquals(2, fooUsers.size()):
assertEquals(users.get(0).getId(), fooUsers.get(0).getId());
assertEquals(users.get(2).getId(), fooUsers.get(1).getId());

看上面的 test case,這樣的程式碼一直重覆,我們該 extract 出來,變成一個 util:

public class UserDaoTest extends AbstractTransactionalSpringContextTests {

public void testFindUsersByName_complex() {
flushThenClearCache();
List<User> fooUsers = dao.findUsersByName("foo");

TestUtils.assertIdEquals(
new User[]{users.get(0), users.get(2)}, fooUsers );
}
}

public class TestUtils {
public static assertIdEquals(Object[] expects, Collection<?> actuals) {
// assert size, and all id equals
}
}

extract 成 TestUtils 之後清爽多了。至於 assertIdEquals() 該怎麼寫就留給各位了。

好,我們要進入第三個課題 - Lazy Initialization Exception,這個是讓全世界 Hibernate developer 恨得牙癢癢的Exception,當然要測!下面是一個 findUserById 的 程式片段:

public class UserDaoTest extends
AbstractTransactionalSpringContextTests {

//一開始只測一點點
public void testFindUserById() {
User user = UserOM.savedUser();
flushThenClearCache();

User found = dao.findUser(user.getId());
assertEquals(user.getId(), found.getId());
}
}

//production 程式:
public class UserDao {
public User findUser(Long id ) {
String hql = " from User where id = ? " ;
return (User) hibernateTemplate.find(hql, id).get(0);
}
}

現在假設 user 底下有多個 groups,是一對多、並且設 lazy=true。如果我們的網頁端的程式呼叫這個 findUser(id) 抽出的 User,而且還讓網頁顯示 user 所屬的 groups 時,就出現 LazyInitializationException 了,因為目前的 findUser(id) 並沒有把 groups 一起 query 出來。解決的方法之一是 HQL 加上 join fetch group。當然,本著 Test First 的精神,再修改 production 程式之前,我們要先把原來的測試改成紅燈:

public class UserDaoTest extends AbstractTransactionalSpringContextTests {

public void testFindUserById() {
User user = UserOM.savedUserWith3Groups();
flushThenClearCache();

User found = dao.findUser(user.getId());
assertEquals(user.getId(), found.getId());

//這樣應該會 throw LazyInitializationException 讓測試變紅燈。
List<Group> groups = found.getGroups() ;
groups.get(0).getGroupName(); // 沒有LazyInitializationException!
}
}

重跑一次測試,啊咧... 沒有紅燈啊?!還是綠燈耶... 又是個無效的測試了... 不是該丟 exception 嗎?仔細看一下產生的 sql,多了一行抽 group 的 sql :

  1. select group0_.id ...中略... from Group group0_ where group0_.user_id= ? select group0_.id ...中略... from Group group0_ where group0_.user_id= ?
    啊!原來是 session 尚未關閉,所以 groups.get(0).getGroupName() 這一行讓 hibernate session 再下了一個 sql。但實際網頁上的情況是 session 早就關了啊!要模擬 session 關閉的狀況,我們得用 endTransaction():
  2. public class UserDaoTest extends AbstractTransactionalSpringContextTests {

    public void testFindUserById() {
    User user = UserOM.savedUserWith3Groups();
    flushThenClearCache();

    User found = dao.findUser(user.getId());

    //加了這一行 transaction 就直接 rollback,session 也跟著一起關了
    endTransaction();

    List<Group> groups = found.getGroups() ;
    groups.get(0).getGroupName(); // 這次真的會 LazyInitializationException 了!
    }
    }

    endTransaction() 是 AbstractTransactionalSpringContextTests 提供的 method,可以讓我們提前終止目前的 transaction,也代表關閉 hibernate session。這一次上面的測試就是紅燈了,耶!現在開始改 production code,加上 join fetch,再跑一次測試就是綠燈了:

    //Test
    public class UserDaoTest extends
    AbstractTransactionalSpringContextTests {

    User user ;

    @Override
    protected void onSetUpInTransaction() throws Exception {
    UserOM.hibernateTemplate = hibernateTemplate;
    user = UserOM.savedUserWith3Groups();
    }

    public void testFindUserById() {
    flushThenClearCache();
    User found = dao.findUser(user.getId());
    TestUtils.assertIdEquals(user, found);
    }

    public void testFindUserById_WithGroups() {
    flushThenClearCache();
    User testGroupLazy = dao.findUser(user.getId());

    endTransaction();

    assertNotNull("group should be fetched",
    testGroupLazy.getGroups().get(0).getGroupName());
    }
    }

    //production 程式:
    public class UserDao {
    public User findUser(Long id ) {
    //加上 left join fetch user.groups
    String hql = " from User user left join fetch user.groups where id = ? " ;
    return (User) hibernateTemplate.find(hql, id).get(0);
    }
    }

    總結:有了 Spring 提供的 AbstractTransactionalSpringContextTests ,讓我們寫第三種測試方法更為容易。由於 hibernate session 的特性使然,很容易寫出無效的空炮彈測試。藉由 flush/clear/endTransaction 等等的處理技巧,可一一有效解決。

註一:fixture 即測試用的假資料。

註二:依據你的資料庫的不同,可能會有不同的結果,如果資料庫產生的 id 不是用 sequence,而是用 increment column (如 MySQL),那 session.save() 當下就會 insert 一筆資料到 db,以取得 id。

本文中常提及的 "production" 字眼意指正式上線使用的程式碼。

2008年5月5日 星期一

監視效能--所有的系統狀態

網路資源真是很多.這篇介紹UNIX的效能狀況的說明.寫得很詳細.但來源我忘了.供大家參考.

我們可以使用uptime來得到整個系統的效能狀態。Uptime 指令得到這個系統已
經開機多久了,有多少使用者登錄,系統在最近 1 分鐘、5 分鐘和 15 分鐘的平
均負載。系統的平均負載顯示有多少工作在執行序列等待,行程準備執行但還未
執行的有多少。下面狀態顯示最近 1 分鐘內系統的平均負載是 0.03,在最近 5
分鐘內系統的平均負載是 0.05,在最近的 15 分鐘內系統的平均負載是 0。我們
也可以使用 cron 定時工作,每隔十五分鐘就記載其平均負載,這樣累計一天,
我們就可以得到最進一天的平均負載。

我們在 8 分鐘後使用 uptime 來觀看系統狀態,其平最近 1 分鐘和最近 5 分鐘的
平均負載變成0。因為 uptime是快照這15分鐘以內的情況,所以只能看到最近
系統的效能。

系統活動報告(system activity reporter)我們簡稱為sar,我們可以使用 sar指令。
這個指令指示sar來計數count 次的範本其間隔為 secs秒。
語法﹕
sar
secs count
sar
-u 1 10這個指令指示sar每隔1秒總共10次來顯示CPU的使用率。
-u的選項告訴sar 來報告CPU的使用率。%user指示CPU花費執行使用者模式
的百分比(使用在應用程式),%nice 花費在非零 nice 優先權的使用者模式,
%system指示花費在核心(系統呼叫),%idle顯示CPU暫停的百分比。一般規則,
假如%system很高,應用程式可能使用系統呼叫。假如%idle 很高而 uptime的

電腦名詞介紹:Grayware(灰色軟體)

Grayware之所以稱為「灰」色軟體,是因為grayware是介於「好軟體」與「壞病毒」之間的灰 色地帶,包括合法的共享軟體、惡意的間諜軟體,以及所有其它的惡意程式或擾人軟體。大部份的grayware都會在未經使用者許可的情況下自行安裝、追 蹤,或是記錄使用者的相關訊息並對外傳送(當然一樣不會告訴使用者)。也有部份的grayware 是偽裝成有用的軟體,欺騙使用者動手安裝的木馬程式。

Grayware的感染來源很多,不論是使用者下載的共享軟體或免費軟體、其他形式的交換檔案、開啟中毒的電子郵件、不小心點選彈出式廣告、進入偽造網站,或是被植入木馬程式等等,都有可能受到感染。

一般來說,grayware 包含以下種類的軟體:

u 廣告軟體 (Adware)

Adware 通常都是夾帶在使用者下載的免費軟體裡,在這個免費軟體執行的時間當中,會顯示彈出式廣告。

u 惡意撥號程式(Dialers)

用來控制使用者的數據機,會被駭客利用來撥打長途電話或是付費電話謀利。

u 惡意遊戲(Gaming)

一些玩笑或是令人煩厭的小遊戲。玩笑軟體(Joke) 通常會更動一些不影響系統的應用程式設定。

u 點對點檔案傳輸軟體(Peer-to-peer, P2P)

進行檔案交換的軟體。雖然 P2P 本身是合法商業用途的通訊協定,但灰色軟體應用程式卻將它做為非法音樂、電影或其他檔案的交換管道。

u 間諜軟體(Spyware)

通常夾帶於免費軟體中。間諜軟體會追蹤並分析使用者的活動,像是瀏覽習慣等。這些訊息都會送回到駭客指定的網站進行記錄與分析。

u 鍵盤側錄軟體(Key Logger)

被安裝在使用者電腦中,會記錄使用者鍵盤上所有的按鍵動作,藉此得知使用者的密碼、信用卡號碼、電子郵件、線上交談、即時訊息及其他相關訊息等。

u 綁架軟體(Hijacker)

會操縱瀏覽器或其他系統設定的程式,它能更改使用者「我的最愛」或書籤所記錄的網站位址、首頁設定,或是功能列選單上的選項等。部份綁架軟體甚至可以更改DNS 設定,不知不覺地將使用者打算瀏覽的網址「繞」到指定的惡意網站去。

u 遠端遙控管理軟體(Remote Administration Tools) 允許外部的使用者存取、更動,或是監控網路上的電腦。

u 工具列(Toolbar)

安裝之後會更改現有的工具列功能,這些程式多半用來監控使用者的瀏覽習慣、將資訊回送至駭客處,或是更改主機的功能。

u 下載(Download)

會在使用者未發覺的情形下,自動允許其他程式進行下載或安裝。通常在電腦的開機階段就會自行啟動,用以安裝廣告、撥號程式或是其他的惡意程式碼。

各種Grayware的感染癥狀

被 grayware 感染後,電腦通常會出現下列癥狀:

•電腦愈來愈慢:grayware 會吃掉許多CPU 與記憶體資源。

•在使用者並未執行相關的網路程序時,從視窗右下角系統狀態列裡的網路連線圖示不斷閃爍。

•彈出式廣告過多:就算是未連結上網際網路,亦未啟動瀏覽器的時候,也會顯示彈出式訊息或廣告。

•瀏覽器首頁被竄改:使用者原設的首頁莫名奇妙地被改掉,就算去重新設定也不一定有用。

•Internet Explorer 的預設搜尋引擎被改:使用者無法使用原來預設的搜尋引擎,並且是由非期待中的搜尋網站提供搜尋結果。

•電話費用爆增:無緣無故地增加了使用者未撥打的付費電話費用。

•防毒程式或是防間諜軟體程式停止運作。老奸巨滑的grayware 會在安裝自己之前,先行關掉資訊安全相關程式。

Grayware的預防之道

既然有這麼多種的可能性,grayware 看來真是令人防不勝防,,在此提供各位一些防範的注意事項,讓大家都能做好預防的功夫。


•養成良好上網習慣,不從網路上下載及安裝軟體。

•養成良好上網習慣,對於網頁中彈跳出的視窗,不予理會,不要點選。

•養成良好上網習慣,登入網站請注意網址,常用網站設成我的最愛,勿相信搜尋網頁的連結。

•安裝防毒軟體,注意病毒碼是否正常更新。

超沒誠意的PCHOME現金積點

這個PCHOME.送個積點送得這麼沒誠意.有時候看到就更生氣..


PChome 現金積點生效通知,詳細資訊如下,歡迎您即刻使用享受折扣!
訂單編號:20080415008930
現金積點:1 點 ( 1 點相當於 1 元折扣 )
生效日期:2008/05/06
有效期間:2008/05/06~2008/06/06

買了半天的東西.才送你一塊錢積點..還要限你一個月內用完..
限制還真多.

電腦中毒後無法上網的解決方法.

電腦中毒後,雖然把病毒清掉了,但還是無法上網,

狀況描述:
用ping的指令來ping,結果有通,但ping出來的結果有點奇怪,出現了?,如下面的情形:
ping tw.yahoo.com
ping ? with 32 bytes of data:
Reply from 202.43.195.52: bytes=32 time=7ms TTL=55
Reply from 202.43.195.52: bytes=32 time=7ms TTL=55
Reply from 202.43.195.52: bytes=32 time=7ms TTL=55

原因:
因為有些惡意程式或間諜軟體安裝一些附加元件至系統的Winsock2機碼中,以利於監視系統的網路訊息。
當你(防毒軟體或反間碟軟體)不小心移除它時,會造成網路中斷或網路不穩定。

解決方法:
在開始功能表,選擇「執行」,打「cmd」,
輸入:netsh winsock reset

如果出現:
順利重設Winsock Catalog。
您必須重新啟動電腦來設成重設。
重新開機就可以了。

ps.以上方法僅適用於windows xp sp2
windows xp sp2以外解決方法
下載 <http://www.pchell.com/downloads/WinsockXPFix.exe> 後直接執行Fix按鈕即可

oracle sql trace

on line trace:
set autotrace on;
select * from app;
set autotrace off;

sql trace:
[sqlplus]
alter session set sql_trace = true;
select * from app;
alter session set sql_trace = false;
[command]
cd c:\oracle\admin\stanley\udump\
tkprof stanley_ora_3444.trc stanley_ora_3444.out aggregate=yes sys=no sort=prscpu
notepad stanley_ora_3444.out

timing:
set timing on;
set timing off;

登入了 vsFTP 但無法上傳檔案

之前也碰過此問題.那時候搞了好久..

後來在網路上看到有人也同樣遇到..在無意中看到一篇說怎麼解決此問題..所以就順便貼下來囉..

基本上只要跟著鳥哥的說明去設定應該就沒問題..

但如果遇到登入後不能上傳試試以下方法:

vi /etc/sysconfig/selinux,將"SELINUX=enforcing"修改成"SELINUX=disabled"。改好後就能正常了