2008年7月14日 星期一

Notepad++

最近開始改用 Notepad++,也在 Port of Ruby Blue Theme 發現一個移植自 TextMate 的 Style,改過來之後發現還蠻好看的。

2008年7月2日 星期三

Spring Framework HibernateTemplate 的 initialize 方法

Spring Framework 提供了 HibernateTemplate,封裝 Hibernate Session 重要的 API,簡化程式的撰寫。

Session 的 load 與 get 有差異,HibernateTemplate 封裝的 load 與 get 當然就有差異,看 HibernateTemplate 的原始程式就很清楚:
public Object load(final Class entityClass, final Serializable id, final LockMode lockMode)
throws DataAccessException {

return executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
if (lockMode != null) {
return session.load(entityClass, id, lockMode);
}
else {
return session.load(entityClass, id);
}
}
});
}

public Object get(final Class entityClass, final Serializable id, final LockMode lockMode)
throws DataAccessException {

return executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
if (lockMode != null) {
return session.get(entityClass, id, lockMode);
}
else {
return session.get(entityClass, id);
}
}
});
}
可是有一件事很有趣,原本使用 Session/Transaction 這樣寫的程式碼:
try
{
tx = session.beginTransaction();
book = (Book) session.load(Book.class, bookID);
Hibernate.initialize(book);
tx.commit();
}
catch (Exception e)
{
if (tx != null) tx.rollback();
e.printStackTrace();
}

不能直接改成底下使用 HibernateTemplate 的寫法:
try
{
book = (Book) hibernateTemplate.load(Book.class, bookID);
hibernateTemplate.initialize(book);
}
catch (Exception e)
{
e.printStackTrace();
}

原因就是 HibernateTemplate 的 Javadoc 所說的:

This class can be considered as direct alternative to working with the raw Hibernate3 Session API (through SessionFactory.getCurrentSession()). The major advantage is its automatic conversion to DataAccessExceptions as well as its capability to fall back to 'auto-commit' style behavior when used outside of transactions. Note that HibernateTemplate will perform its own Session management, not participating in a custom Hibernate CurrentSessionContext unless you explicitly switch "allowCreate" to "false".

可是呢,即使把 allowCreate 設定為 false,還是會有問題:

Caused by: org.hibernate.HibernateException: load is not valid without active transaction

也就是沒有把 load 包在 Transaction 裡頭。

可是如果還要寫出 Transaction 的程式碼,那就麻煩了。

所以呢,就是透過 Spring Framework 提供的 AOP,掛上 Transaction,這時候就不必去設定什麼 allowCreate,直接就可以正常運作了!

2008年7月1日 星期二

Hibernate Session 的 get 與 load

Hibernate Session 為了節省一次 Database Roundtrip,所以:
  • load 只是產生一個 Proxy,或是直接取用目前 Session 中已有的 Cache,不會真的連到 Database
  • get 才會真的連到資料庫,取得一個 Fully Initialized POJO
以前只注意到 get 找不到資料的時候會回傳 null,可是 load 不會。不管 Database 有沒有那筆紀錄,load 的傳回值一定不是 null。詳細探究原因,才知道 load 根本不會殺到 Database,也就沒有機會給 null。

org.hibernate.Session 的 Javadoc 說法是:
  • load:Return the persistent instance of the given entity class with the given identifier, assuming that the instance exists. You should not use this method to determine if an instance exists (use get() instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.
  • get:Return the persistent instance of the given named entity with the given identifier, or null if there is no such persistent instance. (If the instance, or a proxy for the instance, is already associated with the session, return that instance or proxy.)
這邊看不出為什麼 get 不會拿到 Proxy,但是從 org.hibernate.impl.SessionImpl 的程式碼可以看到:
public Object load(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
return load( entityClass.getName(), id, lockMode );
}

public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
fireLoad( event, LoadEventListener.LOAD );
return event.getResult();
}

public Object get(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
return get( entityClass.getName(), id, lockMode );
}

public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
fireLoad(event, LoadEventListener.GET);
return event.getResult();
}

LoadEventListener 有 LOAD 與 GET 兩種情形。

org.hibernate.event.LoadEventListener 的程式碼中可以看出:
public static final LoadType GET = new LoadType("GET")
.setAllowNulls(true)
.setAllowProxyCreation(false)
.setCheckDeleted(true)
.setNakedEntityReturned(false);

public static final LoadType LOAD = new LoadType("LOAD")
.setAllowNulls(false)
.setAllowProxyCreation(true)
.setCheckDeleted(true)
.setNakedEntityReturned(false);

其中,GET 的時候,setAllowProxyCreation 的參數是 false,應該就表示 GET 的時候不允許產生 Proxy 物件。setAllowNulls 的參數是 true,這應該就是為什麼找不到資料的時候可以回傳 null 的原因吧!

2008年6月30日 星期一

兩個 Blogspot 工具

FORMAT MY SOURCE CODE FOR BLOGGING 這個 JavaScript 程式做的還蠻方便的,不然我還不知道要如何張貼程式碼到 Blogspot 呢!後來又逛到文章中引用程式碼的作法,感覺也不錯。

Setup and configuration for New Blogger Tag Cloud / Label Cloud 讓我的 Blogspot 網誌也可以有 Tag Cloud 功能耶!

JBoss AS 搭配 MySQL XA DataSource 設定方式

多方參考網路上的設定,測試之後,發現以下的內容就足夠了:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<xa-datasource>
<jndi-name>YourDataSourceName</jndi-name>
<xa-datasource-class>
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
</xa-datasource-class>
<xa-datasource-property name="URL">
jdbc:mysql://localhost:3306/DBName
</xa-datasource-property>
<user-name>YourUsername</user-name>
<password>YourPassword</password>
<track-connection-by-tx>
true
</track-connection-by-tx>
<new-connection-sql>
set autocommit=1
</new-connection-sql>
<no-tx-separate-pools>
true
</no-tx-separate-pools>
<exception-sorter-class-name>
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</xa-datasource>
</datasources>
檔案名稱按照 JBoss AS 的規定,必須以「-xa-ds.xml」結尾,比方說「yourdb-xa-ds.xml」,放在使用組態下的 deploy 目錄即可。

這邊有一件事要注意到,因為 JBoss 本身處理的不夠完善,所以像底下這樣 Pretty-Format 過的寫法會出問題:
<xa-datasource-property name="URL">
jdbc:mysql://localhost:3306/DBName
</xa-datasource-property>
必須改成底下的寫法:
<xa-datasource-property name="URL">jdbc:mysql://localhost:3306/DBName</xa-datasource-property>
這種 Bug 至少在 2003 年 8 月 JBoss 就已經承認,詳見這裡,可是到現在已經 5.0.1 GA 版本了,還是依然存在。

Hibernate 與 MySQL 的 XA DataSource

這幾天一直在測 Hibernate 與 JTA,所以就一直試著設定 MySQL 的 MysqlXADataSource。

奇怪的是,JDBC 搭配 XA DataSource 一直沒問題,可是 Hibernate 搭配 XA DataSource 一直都不成功,不斷顯示 null source 這個訊息。

Google 搜尋了半天,好不容易看到有人也有類似的問題。解法也很爆笑,把 MySQL Connector/J 從 5.1.6 Downgrade 到 5.0.8 版,就搞定了!

2008年6月25日 星期三

科技宅男+氣質正妹+冷眼路人的3P羅生門

以下是昨天晚上發生在某星x客的真實事件,如果雷同,那一定是因為你自己對號入座的緣故。

科技宅男:
小姐,麻煩幫我看一下我的電腦,我下去買杯咖啡。

氣質正妹:
喔!

冷眼路人內心的OS:
奇怪,難不成這樣說一句,其他人就得幫你看好電腦喔?
東西掉了我要賠嗎?
我要尿尿怎麼辦?
我要先走怎麼辦?
萬一你像丟棄嬰一樣不回來了怎麼辦?

五分鐘之後,宅男回來了。

科技宅男:
小姐,謝謝。

氣質正妹:
喔!

科技宅男:
小姐,你有愛趴喔!你那是幾G的?8G還是4G?

氣質正妹:
喔!8G!

科技宅男:
小姐,8G你裝的完喔?

氣質正妹:
還好,謝謝!

正妹戴上耳機,不想理了。

冷眼路人內心的OS:
人家已經說謝謝了,不要再盧了!

科技宅男:
小姐,你可以買4G的,不然也可以買Nano,小小的很方便,價格也差不多。

正妹拿下耳機。

氣質正妹:
喔!我知道。謝謝!

冷眼路人內心的OS:
已經說第二次謝謝了,你聽不懂嗎?

科技宅男:
小姐,那妳的MP3都是下載的,還是妳都有買原版CD?

氣質正妹:
喔!我有買CD。

科技宅男:
是喔!小姐,這年頭會買原版CD的人真的太少了!那妳都聽哪一類的音樂?

氣質正妹:
喔!都聽。謝謝!

正妹戴上耳機,不想理了。

科技宅男:
是喔!小姐,我都聽爵士的!

正妹拿下耳機。

氣質正妹:
喔!謝謝!

正妹戴上耳機,不想理了。

科技宅男:
小姐,那妳知不知道愛趴可以下載專輯封面,這樣看起來很好看。

正妹拿下耳機。

氣質正妹:
喔!我不需要,謝謝!

冷眼路人內心的OS:
已經不知道說第幾次謝謝了,你還是聽不懂嗎?

正妹戴上耳機,不想理了。

科技宅男:
小姐,那妳知不知道愛趴可以播影片?

正妹拿下耳機。

氣質正妹:
嗯。

科技宅男:
我有一陣子花了一堆時間把影片轉成愛趴可以播的格式,Resolution看起來好好喔!

氣質正妹:
嗯。謝謝!

正妹戴上耳機,不想理了。

科技宅男:
小姐,那妳知不知道愛趴可以可以聽廣播?我告訴妳喔,有一種東西叫做Podcasting,妳知不知道?

正妹拿下耳機。

氣質正妹:
嗯。

科技宅男:
小姐,那個Podcasting啊,就是...,對了,妳知道iTunes吧?你的MP3都是用iTunes管理的吧?

氣質正妹:
嗯。謝謝!

正妹戴上耳機,不想理了。

科技宅男:
小姐,那妳有在用電腦吧?

正妹拿下耳機。

氣質正妹:
嗯,我很少用。

科技宅男:
是喔,沒關係。那妳有在用MSN嗎?

氣質正妹:
沒有,很少。

冷眼路人內心的OS:
終於露出你的目的了吧!

科技宅男:
是喔,沒關係。那我給妳我的MSN,如果有問題的話,妳可以MSN我。你有紙筆嗎?借我寫一下...。
這是我的MSN,如果不會操作的話,我可以教妳。
說不定有緣的話,我們還會碰面喔!

冷眼路人內心的OS:
了不起!應該不是有緣,是不幸吧!

這時氣質正妹也露了一手,拿起了手機。

氣質正妹:
喂,XX啊!我這邊差不多好了,你那邊呢?喔!你也好了啊!什麼?你已經到了啊!好,那我下去找你,881!

然後,氣質正妹東西收一收,頭也不回的就走了,留下一臉錯愕的科技宅男。

科技宅男不到五分鐘,也走了!

結論就是:

我還真是溫良恭儉讓啊!上面的手法,我還真是作不出來也說不出口啊!

以上,報告完畢!