摘要: 提起Java內(nèi)部類(Inner Class)可能很多人不太熟悉,實際上類似的概念在C++里也有,那就是嵌套類(Nested Class),關(guān)于這兩者的區(qū)別與聯(lián)系,在下文中會有對比。內(nèi)部類從表面上看,就是在類中又定義了一個類(下文會看到,內(nèi)部類可以在很多地方定義),而實際上并沒有那么簡單,乍看上去內(nèi)部類似乎有些多余,它的用處對于初學者來說可能并不是那么顯著,但是隨著對它的深入了解,你會發(fā)現(xiàn)Java的...
閱讀全文
posted @
2010-06-30 14:26 J2EE學習筆記 閱讀(307) |
評論 (0) |
編輯 收藏
在JScript的眾多運算符里,提供了三個邏輯運算符&&、||和!,噢?! 是高級語言都提供的
。按我們對邏輯運算的正常認識,邏輯運算的結(jié)果因該是ture或者false。但是JScript的邏輯運算卻不完全是這么定義的,這里只有!運算符總是返回true|false,而||和&&運算比較的好玩。
JScript對于邏輯運算的true|false是這么定義的:
- 所有對象都被認為是 true。
- 字符串當且僅當為空(""或'')時才被認為是 false。
- null 和未定義的均被認為是 false。
- 數(shù)字當且僅當為 0 時才是 false。
可是邏輯運算符||和&&雖然遵循上面的定義規(guī)則,但是它們返回的值卻很有意思。
對于&&運算,按照上面的規(guī)則,表達式 if ( 'abc' && '123' && new Date() ) 是執(zhí)行true分支,可是這個表達式如果寫成:
var value = 'abc' && '123' && new Date();
結(jié)果value=Fri Jan 21 00:01:17 UTC+0800 2005,原它從左到右檢測,如果到了最后一個表達式也是為true的,就返回那個表達式。
對于||運算同理,對于下面的表達式:
var value1 = 'abc' || '123' || null || false;
var value2 = null || '' || false || 'ok';
結(jié)果value1='abc',value2='ok'。這是因為||運算會有"短路"特性,他也是從左向右檢測,只不過它是一但發(fā)現(xiàn)有為true的值,就立即返回該表達式。
這樣的特性可以幫組我們寫出精簡的代碼,可是同時也帶來代碼不便于閱讀維護的問題。
由于我手頭暫時沒有NS和moz什么的瀏覽器,不知道標準JavaScript是否也是這樣支持的?如果您方便的話,請告如我運行后的結(jié)果
posted @
2010-05-13 15:11 J2EE學習筆記 閱讀(188) |
評論 (0) |
編輯 收藏
有時你可能需要對變量進行類型檢查,或者判斷變量是否已定義。有兩種方法可以使用:typeof函數(shù)與constructor屬性。
typeof函數(shù)的用法可能不用我多說,大家都知道怎么用。而constructor屬性大家可能就陌生點。在《精通JavaScript》這本書中有提到construct的用法,但我用自己的幾個瀏覽器(IE7.0 / Firefox1.9 / Opera9.50)測試的結(jié)果卻和書上說的不一樣。但是仍然是有辦法通過constructor屬性來檢查變量類型的。
這里先補充一下,為什么明明有typeof函數(shù)可以很方便地用來檢測類型,還要用constructor呢?
因為typeof會把所有的數(shù)組類型以及用戶自定義類型判斷為object,從而無法知道更確切的信息。而constructor卻可以解決這個問題。
ok,明白了我們?yōu)槭裁匆胏onstructor,現(xiàn)在讓我?guī)Т蠹乙徊讲秸J識一下typeof和constructor用法之間的差異吧~
首先我們運行一下下面這段代碼:
var i;
alert(typeof(i));
alert(i.constructor);
這3行代碼告訴你什么情況下可以用constructor。
你可以看到第2行返回了字符串'undefined',而第三行則發(fā)生了錯誤,原因是i變量還沒有類型定義,自然也沒有constructor的存在。
從這一點上看,typeof可以檢查到變量是否有定義,而construct只能檢查已定義變量的類型。
再運行一下下面這段代碼:
var i = 2;
alert(typeof(i));
alert(i.constructor);
alert(typeof(i.constructor));
你會看到第2行返回了字符串'number’,第3行返回了一串類似函數(shù)定義的代碼字符串(這就是跟《精通JavaScript》一書中介紹的不一樣的地方)。
我們再用typeof檢查一下constructor到底是個什么樣類型的屬性,第4行返回結(jié)果'function',也就是說,實際上constructor是一個函數(shù),更確切地說是一個構(gòu)造函數(shù)。這時你就可以知道,為什么constructor可以檢查出各種類型了。
有經(jīng)驗的程序員看到這里應(yīng)該知道要怎么利用constructor來檢查變量類型了。方法有多種,這里提供一種比較容易理解的方法。
其實想法很簡單,就是把construcor轉(zhuǎn)化為字符串,通過尋找匹配字符串(function名)來確定是否指定類型。如下例子:

function user()
{};
var i = new user();
alert((i.constructor+'').match(/user/) == null);
這僅僅是個簡單的例子。如果返回true則變量i不是user類型,返回false則變量是user類型。
當然,這樣檢測是不夠精確的,比如其實他是一個myuser類型的時候,同樣會被認為是user類。所以你需要書寫更精確的正則表達式去進行匹配。
可以這樣簡單改進你的正則表達式:
/function user\(\)/
替換上面代碼段中的/user/。當然,如果你的構(gòu)造函數(shù)原型是user(a),那么應(yīng)該這樣書寫你的正則表達式:
/function user\(a\)/
到這里你應(yīng)該知道怎樣使用constructor類型去檢查變量類型了吧?
ok,最后再提個醒,如果你要用基于constructor的方法去檢查一些基本類型,如
Object / Array / Function / String / Number / Boolean
在你的正則表達式中,一定要將這些單詞的首字母大寫!!而如果該類型是自定義類型,則根據(jù)你定義的時候標識符的寫法確定。
posted @
2010-04-14 14:30 J2EE學習筆記 閱讀(321) |
評論 (0) |
編輯 收藏

/** *//**
使用三種Callback接口作為參數(shù)的query方法的返回值不同:
以ResultSetExtractor作為方法參數(shù)的query方法返回Object型結(jié)果,要使用查詢結(jié)果,我們需要對其進行強制轉(zhuǎn)型;
以RowMapper接口作為方法參數(shù)的query方法直接返回List型的結(jié)果;
以RowCallbackHandler作為方法參數(shù)的query方法,返回值為void;
RowCallbackHandler和RowMapper才是我們最常用的選擇
* @author Administrator
*
*/

public class SpringTest
{

/** *//**
* 返回結(jié)果是List里裝Map,使用參數(shù),使用回調(diào) RowMapperResultSetExtractor用于處理單行記錄,
* 它內(nèi)部持有一個RowMapper實例的引用,當處理結(jié)果集的時候, 會將單行數(shù)據(jù)的處理委派給其所持有的RowMapper實例,而其余工作它負責
*/

public void getListRowMapperResultSetExtractor()
{
ApplicationContext context = new FileSystemXmlApplicationContext(
"src/database_config.xml");
// E:\demoworkspace\spring 為工程主目錄
JdbcTemplate jt = new JdbcTemplate((DataSource) context
.getBean("oracleDataSourceTest")); // 測試用的方法

Object[] arg = new Object[]
{ 10 };
List list = (ArrayList) jt.query("select * from region where rownum<?",

arg, new RowMapperResultSetExtractor(new RowMapper()
{
public Object mapRow(ResultSet rs, int index)

throws SQLException
{
Map u = new HashMap(); //可以是自己的JavaBean值對象(簡單Java對象POJO)
u.put("region_id", rs.getString("region_id"));
u.put("region_name", rs.getString("region_name"));
return u;
}
}));
Iterator it = list.iterator();

while (it.hasNext())
{
Map map = (Map) it.next();
System.out.println(map.toString());
}
}

/** *//**返回結(jié)果是List里裝Map,不使用參數(shù),使用回調(diào)
使用RowMapper比直接使用ResultSetExtractor要方便的多,只負責處理單行結(jié)果就行,現(xiàn)在,我們只需要將單行的結(jié)果組裝后返回就行,
剩下的工作,全部都是JdbcTemplate內(nèi)部的事情了。 實際上,JdbcTemplae內(nèi)部會使用一個ResultSetExtractor實現(xiàn)類來做其余的工作,
畢竟,該做的工作還得有人做不是?!
*/

public void getListRowMapper()
{
ApplicationContext context = new FileSystemXmlApplicationContext(
"src/database_config.xml");
JdbcTemplate jt = new JdbcTemplate((DataSource) context
.getBean("oracleDataSourceTest"));
List list = jt.query(

"select * from region where rownum<10", new RowMapper()
{
public Object mapRow(ResultSet rs, int index)

throws SQLException
{
Map u = new HashMap();
u.put("region_id", rs.getString("region_id"));
u.put("region_name", rs.getString("region_name"));
return u;
}
});
Iterator it = list.iterator();

while (it.hasNext())
{
Map map = (Map) it.next();
System.out.println(map.toString());
}
}
// 返回記錄集

/** *//**
RowCallbackHandler雖然與RowMapper同是處理單行數(shù)據(jù),不過,除了要處理單行結(jié)果,它還得負責最終結(jié)果的組裝和獲取工作,
在這里我們是使用當前上下文聲明的List取得最終查詢結(jié)果, 不過,我們也可以單獨聲明一個RowCallbackHandler實現(xiàn)類,
在其中聲明相應(yīng)的集合類,這樣,我們可以通過該RowCallbackHandler實現(xiàn)類取得最終查詢結(jié)果
*/

public void getListRowCallbackHandler()
{
ApplicationContext context = new FileSystemXmlApplicationContext(
"src/database_config.xml");
JdbcTemplate jt = new JdbcTemplate((DataSource) context
.getBean("oracleDataSourceTest"));
String sql = "select * from region where region_id>?";
final List<Map> list=new ArrayList<Map>(); //一定要用final定義

Object[] params = new Object[]
{ 0 };

jt.query(sql, params, new RowCallbackHandler()
{

public void processRow(ResultSet rs) throws SQLException
{
Map u = new HashMap();
u.put("region_id", rs.getString("region_id"));
u.put("region_name", rs.getString("region_name"));
list.add(u);
}
});
Iterator it = list.iterator();

while (it.hasNext())
{
Map map = (Map) it.next();
System.out.println(map.toString());
}
}
posted @
2010-03-10 10:27 J2EE學習筆記 閱讀(569) |
評論 (0) |
編輯 收藏
摘要: 1.springJdbcContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
&nb...
閱讀全文
posted @
2010-03-09 19:10 J2EE學習筆記 閱讀(2017) |
評論 (0) |
編輯 收藏
很多朋友在深入的接觸JAVA語言后就會發(fā)現(xiàn)這樣兩個詞:反射(Reflection)和內(nèi)省(Introspector),經(jīng)常搞不清楚這到底是怎么回事,在什么場合下應(yīng)用以及如何使用?今天把這二者放在一起介紹,因為它們二者是相輔相成的。
反射
相對而言,反射比內(nèi)省更容易理解一點。用一句比較白的話來概括,反射就是讓你可以通過名稱來得到對象(類,屬性,方法)的技術(shù)。例如我們可以通過類名來生成一個類的實例;知道了方法名,就可以調(diào)用這個方法;知道了屬性名就可以訪問這個屬性的值。
還是寫兩個例子讓大家更直觀的了解反射的使用方法:
// 通過類名來構(gòu)造一個類的實例
Class cls_str = Class.forName( "java.lang.String" );
// 上面這句很眼熟,因為使用過 JDBC 訪問數(shù)據(jù)庫的人都用過 J
Object str = cls_str.newInstance();
// 相當于 String str = new String();
// 通過方法名來調(diào)用一個方法
String methodName = "length" ;
Method m = cls_str.getMethod(methodName, null );
System.out.println( "length is " + m.invoke(str, null ));
// 相當于 System.out.println(str.length());
上面的兩個例子是比較常用方法。看到上面的例子就有人要發(fā)問了:為什么要這么麻煩呢?本來一條語句就完成的事情干嗎要整這么復(fù)雜?沒錯,在上面的例子中確實沒有必要這么麻煩。不過你想像這樣一個應(yīng)用程序,它支持動態(tài)的功能擴展,也就是說程序不重新啟動但是可以自動加載新的功能,這個功能使用一個具體類來表示。首先我們必須為這些功能定義一個接口類,然后我們要求所有擴展的功能類必須實現(xiàn)我指定的接口,這個規(guī)定了應(yīng)用程序和可擴展功能之間的接口規(guī)則,但是怎么動態(tài)加載呢?我們必須讓應(yīng)用程序知道要擴展的功能類的類名,比如是test.Func1,當我們把這個類名(字符串)告訴應(yīng)用程序后,它就可以使用我們第一個例子的方法來加載并啟用新的功能。這就是類的反射,請問你有別的選擇嗎?
內(nèi)省
內(nèi)省是Java語言對Bean類屬性、事件的一種缺省處理方法。例如類A中有屬性name,那我們可以通過getName,setName來得到其值或者設(shè)置新的值。通過getName/setName來訪問name屬性,這就是默認的規(guī)則。Java中提供了一套API用來訪問某個屬性的getter/setter方法,通過這些API可以使你不需要了解這個規(guī)則(但你最好還是要搞清楚),這些API存放于包java.beans中。
一般的做法是通過類Introspector來獲取某個對象的BeanInfo信息,然后通過BeanInfo來獲取屬性的描述器(PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應(yīng)的getter/setter方法,然后我們就可以通過反射機制來調(diào)用這些方法。下面我們來看一個例子,這個例子把某個對象的所有屬性名稱和值都打印出來:

/**//*
* Created on 2004-6-29
*/

package demo;


import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;



/** *//**
* 內(nèi)省演示例子
* @author liudong
*/


public class IntrospectorDemo
{
String name;

public static void main(String[] args) throws Exception
{
IntrospectorDemo demo = new IntrospectorDemo();
demo.setName( "Winter Lau" );

// 如果不想把父類的屬性也列出來的話,
// 那 getBeanInfo 的第二個參數(shù)填寫父類的信息
BeanInfo bi = Introspector.getBeanInfo(demo.getClass(), Object. class );
PropertyDescriptor[] props = bi.getPropertyDescriptors();

for ( int i=0;i<props.length;i++)
{
System.out.println(props[i].getName()+ "=" +
props[i].getReadMethod().invoke(demo, null ));
}

}


public String getName()
{
return name;
}


public void setName(String name)
{
this .name = name;
}
}
Web開發(fā)框架Struts中的FormBean就是通過內(nèi)省機制來將表單中的數(shù)據(jù)映射到類的屬性上,因此要求FormBean的每個屬性要有g(shù)etter/setter方法。但也并不總是這樣,什么意思呢?就是說對一個Bean類來講,我可以沒有屬性,但是只要有g(shù)etter/setter方法中的其中一個,那么Java的內(nèi)省機制就會認為存在一個屬性,比如類中有方法setMobile,那么就認為存在一個mobile的屬性,這樣可以方便我們把Bean類通過一個接口來定義而不用去關(guān)系具體實現(xiàn),不用去關(guān)系Bean中數(shù)據(jù)的存儲。比如我們可以把所有的getter/setter方法放到接口里定義,但是真正數(shù)據(jù)的存取則是在具體類中去實現(xiàn),這樣可提高系統(tǒng)的擴展性。
總結(jié)
將Java的反射以及內(nèi)省應(yīng)用到程序設(shè)計中去可以大大的提供程序的智能化和可擴展性。有很多項目都是采取這兩種技術(shù)來實現(xiàn)其核心功能,例如我們前面提到的Struts,還有用于處理XML文件的Digester項目,其實應(yīng)該說幾乎所有的項目都或多或少的采用這兩種技術(shù)。在實際應(yīng)用過程中二者要相互結(jié)合方能發(fā)揮真正的智能化以及高度可擴展性。
posted @
2010-02-04 13:42 J2EE學習筆記 閱讀(376) |
評論 (1) |
編輯 收藏
摘要: 原文出處:http://blog.chenlb.com/2008/11/join-or-countdownlatch-make-main-thread-wait-all-sub-thread.html
在編寫多線程的工作中,有個常見的問題:主線程(main) 啟動好幾個子線程(task)來完成并發(fā)任務(wù),主線程要等待所有的子線程完成之后才繼續(xù)執(zhí)行main的其它任務(wù)。
默認主線程退出時其它子線程不...
閱讀全文
posted @
2010-01-26 18:00 J2EE學習筆記 閱讀(1207) |
評論 (0) |
編輯 收藏
這是jQuery里常用的2個方法。
他們2者功能是完全不同的,而初學者往往會被誤導。
首先 我們看.find()方法:
現(xiàn)在有一個頁面,里面HTML代碼為:
<div class="css">
<p class="rain">測試1</p>
</div>
<div class="rain">
<p>測試2</p>
</div>
如果我們使用find()方法:
var $find = $("div").find(".rain");
alert( $find.html() );
將會輸出:

如果使用filter()方法:
var $filter = $("div").filter(".rain");
alert( $filter.html() );
將會輸出:

也許你已經(jīng)看出它們的區(qū)別了。
find()會在div元素內(nèi) 尋找 class為rain 的元素。
而filter()則是篩選div的class為rain的元素。
一個是對它的子集操作,一個是對自身集合元素篩選。
另外find()其實還可以用選擇器表示:
var $select = $("div .rain");
posted @
2009-11-09 16:00 J2EE學習筆記 閱讀(376) |
評論 (0) |
編輯 收藏
以前在Windows上ssh登錄一直都是用putty,雖然它簡單小巧,但畢竟缺少很多特性。今天試了一下SecureCRT,感覺用起來比Putty好多了,但SecureCRT默認的字體超難看,而且中文字體設(shè)置也比較麻煩一點,在這里記錄一下以后可能還用得到。
- 在“會話選項”的“終端->仿真”里面選“Linux”,如果需要顯示顏色的話需要把“ANSI顏色”選上
- 在“外觀->字體”中選擇喜歡的字體,但這里對字體是有要求的,只有等寬字體才行。如果要正常顯示中文的話,所選擇的字體還必須包含中文字符。
- 另外就是根據(jù)你要登錄的主機的字符編碼選擇字符編碼,一般是 “UTF-8″
簡單的幾步下來就設(shè)置好了,如果還有亂碼的話就退出然后重新登陸一下。如果你想所有的連接都使用這個默認配置,可以在“全局選項”中設(shè)置“默認的會話選項”,這樣以后新建的連接會自動應(yīng)用上面的設(shè)置了。
PS:
以前用putty的時候,字體就直接用我在Linux最愛的Monaco,但在SecureCRT中用Monaco字體的話,中文會顯示為亂碼,這是因為Monaco字體中不包含中文字符,而SecureCRT也不會自動的選擇系統(tǒng)默認的中文字體。
為了解決這個問題,我們只要去找一個包含中文的等寬字體來用,我從網(wǎng)上找了一個Consolas和雅黑的混合字體,雖然沒有Monaco好看,但效果也還不錯。這里有個地方需要注意一下,在選擇這個字體的字體選擇對話框中,字體的默認字符集是“西方”,需要改成CHINESE_GB2312。
如果你也想用這個字體的話,可以從這里下載。
posted @
2009-09-14 14:14 J2EE學習筆記 閱讀(6058) |
評論 (0) |
編輯 收藏
這些操作對經(jīng)常使用hibernate的同學已經(jīng)很熟悉了,我也經(jīng)常用但一些細節(jié)并不了解,
最近遇到問題才開始有看了一下。
在讀完robbin的這兩個精華貼的時候,感覺清晰了很多,確實好文章。
http://www.javaeye.com/topic/2712
http://www.javaeye.com/topic/1604?page=1
還有這個精華貼
http://www.javaeye.com/topic/7484
也很不錯。
里面總結(jié)的很好了,我結(jié)合以上三個帖子、自己的試驗(版本hibernate-3.0.5)和Hibernate文檔也總結(jié)了一點,加深理解。希望對剛開始學Hibernate的同學有所幫助。
一、saveorUpdate與unsaved-value
到底是sava還是update
Hibernate需要判斷被操作的對象究竟是一個已經(jīng)持久化過的持久對象還是臨時對象。
1).主鍵Hibernate的id generator產(chǎn)生
<id name="id" type="java.lang.Long">
<column name="ID" precision="22" scale="0" />
<generator class="increment" />
</id>
Project project = new Project();
project.setId(XXX);
this.projectDao.saveOrUpdate(project);
1、默認unsaved-value="null"
主鍵是對象類型,hebernate判斷project的主鍵是否位null,來判斷project是否已被持久化
是的話,對project對象發(fā)送save(project),
若自己設(shè)置了主鍵則直接生成update的sql,發(fā)送update(project),即便數(shù)據(jù)庫里沒有那條記錄。
主鍵是基本類型如int/long/double/
自己設(shè)置unsaved-null="0"。
所以這樣的話save和update操作肯定不會報錯。
2、unsaved-value="none",
由于不論主鍵屬性為任何值,都不可能為none,因此Hibernate總是對project對象發(fā)送update(project)
3、unsaved-value="any"
由于不論主鍵屬性為任何值,都肯定為any,因此Hibernate總是對project對象發(fā)送save(project),hibernate生成主鍵。
Hibernate文檔中寫到
saveOrUpdate()完成了如下工作:
如果對象已經(jīng)在這個session中持久化過了,什么都不用做
如果對象沒有標識值,調(diào)用save()來保存它
如果對象的標識值與unsaved-value中的條件匹配,調(diào)用save()來保存它
如果對象使用了版本(version或timestamp),那么除非設(shè)置unsaved-value="undefined",版本檢查會發(fā)生在標識符檢查之前.
如果這個session中有另外一個對象具有同樣的標識符,拋出一個異常
2).主鍵由自己來賦值
<id name="id" type="java.lang.Long">
<column name="ID" precision="22" scale="0" />
<generator class="assigned" />
</id>
Project project = new Project();
project.setId(XXX);
this.projectDao.saveOrUpdate(project);
1、默認unsaved-value="null"
這時有所不同,hibernate會根據(jù)主鍵產(chǎn)生一個select,來判斷此對象是否已被持久化
已被持久化則update,未被持久化則save。
2、unsaved-value="none",update對象,同上
3、unsaved-value="any" ,save對象,
如果自己自己設(shè)置的ID在數(shù)據(jù)庫中已存在,則報錯。
二、save與update操作
顯式的使用session.save()或者session.update()操作一個對象的時候,實際上是用不到unsaved-value的
在同一Session,save沒什么可說得
update對象時, 最直接的更改一個對象的方法就是load()它,保持Session打開,然后直接修改即可:
Session s =…
Project p = (Project) sess.load(Project.class, id) );
p.setName(“test”);
s.flush();
不用調(diào)用s.update(p);hibernate能察覺到它的變化,會自動更新。當然顯示調(diào)用的話也不會錯
Hibernate文檔中寫到
update()方法在下列情形下使用:
程序在前面的session中裝載了對象
對象被傳遞到UI(界面)層
對該對象進行了一些修改
對象被傳遞回業(yè)務(wù)層
應(yīng)用程序在第二個session中調(diào)用update()保存修改
三、delete操作
刪除時直接自己構(gòu)造一個project即可刪除
this.projectDao.delete(preojct);
以前刪除我是這樣寫的
public void deleteProject(String id) {
Project project = (Project) this.projectDao.get(Project.class, id);
if (project != null) {
this.projectDao.delete(project);
}
即這樣也是可以的
Project project = new Project();
project.setId(id);
this.projectDao.delete(project).
如果有級聯(lián)關(guān)系,需要把級聯(lián)的子類也構(gòu)造出來add進去,同樣可以刪除。
好了,羅嗦的夠多了。
posted @
2009-08-27 14:44 J2EE學習筆記 閱讀(496) |
評論 (0) |
編輯 收藏
當你顯式的使用session.save()或者session.update()操作一個對象的時候,實際上是用不到unsaved-value的。某些情況下(父子表關(guān)聯(lián)保存),當你在程序中并沒有顯式的使用save或者update一個持久對象,那么Hibernate需要判斷被操作的對象究竟是一個已經(jīng)持久化過的持久對象,是一個尚未被持久化過的內(nèi)存臨時對象。例如:
Session session =
;
Transaction tx =
;

Parent parent = (Parent); session.load(Parent.class, id);;

Child child = new Child();;
child.setParent(parent);;
child.setName("sun");;

parent.addChild(child);;
s.update(parent);;

s.flush();;
tx.commit();;
s.close();;
在上例中,程序并沒有顯式的session.save(child); 那么Hibernate需要知道child究竟是一個臨時對象,還是已經(jīng)在數(shù)據(jù)庫中有的持久對象。如果child是一個新創(chuàng)建的臨時對象(本例中就是這種情況),那么Hibernate應(yīng)該自動產(chǎn)生session.save(child)這樣的操作,如果child是已經(jīng)在數(shù)據(jù)庫中有的持久對象,那么Hibernate應(yīng)該自動產(chǎn)生session.update(child)這樣的操作。
因此我們需要暗示一下Hibernate,究竟child對象應(yīng)該對它自動save還是update。在上例中,顯然我們應(yīng)該暗示Hibernate對child自動save,而不是自動update。那么Hibernate如何判斷究竟對child是save還是update呢?它會取一下child的主鍵屬性 child.getId() ,這里假設(shè)id是 java.lang.Integer類型的。如果取到的Id值和hbm映射文件中指定的unsave-value相等,那么Hibernate認為child是新的內(nèi)存臨時對象,發(fā)送save,如果不相等,那么Hibernate認為child是已經(jīng)持久過的對象,發(fā)送update。
unsaved-value="null" (默認情況,適用于大多數(shù)對象類型主鍵 Integer/Long/String/...)
當Hibernate取一下child的Id,取出來的是null(在上例中肯定取出來的是null),和unsaved-value設(shè)定值相等,發(fā)送save(child)
當Hibernate取一下child的id,取出來的不是null,那么和unsaved-value設(shè)定值不相等,發(fā)送update(child)
例如下面的情況:
Session session =
;
Transaction tx =
;

Parent parent = (Parent); session.load(Parent.class, id);;
Child child = (Child); session.load(Child.class, childId);;

child.setParent(parent);;
child.setName("sun");;

parent.addChild(child);;
s.update(parent);;

s.flush();;
tx.commit();;
s.close();;
child已經(jīng)在數(shù)據(jù)庫中有了,是一個持久化的對象,不是新創(chuàng)建的,因此我們希望Hibernate發(fā)送update(child),在該例中,Hibernate取一下child.getId(),和unsave-value指定的null比對一下,發(fā)現(xiàn)不相等,那么發(fā)送update(child)。
BTW: parent對象不需要操心,因為程序顯式的對parent有l(wèi)oad操作和update的操作,不需要Hibernate自己來判斷究竟是save還是update了。我們要注意的只是child對象的操作。另外unsaved-value是定義在Child類的主鍵屬性中的。
<class name="Child" table="child">
<id column="id" name="id" type="integer" unsaved-value="null">
<generator class="identity"/>
</id>


</class>
如果主鍵屬性不是對象型,而是基本類型,如int/long/double/...,那么你需要指定一個數(shù)值型的unsaved-value,例如:
unsaved-value="0"
在此提醒大家,很多人以為對主鍵屬性定義為int/long,比定義為Integer/Long運行效率來得高,認為基本類型不需要進行對象的封裝和解構(gòu)操作,因此喜歡把主鍵定義為int/long的。但實際上,Hibernate內(nèi)部總是把主鍵轉(zhuǎn)換為對象型進行操作的,就算你定義為int/long型的,Hibernate內(nèi)部也要進行一次對象構(gòu)造操作,返回給你的時候,還要進行解構(gòu)操作,效率可能反而低也說不定。因此大家一定要扭轉(zhuǎn)一個觀點,在Hibernate中,主鍵屬性定義為基本類型,并不能夠比定義為對象型效率來的高,而且也多了很多麻煩,因此建議大家使用對象型的Integer/Long定義主鍵。
unsaved-value="none"和
unsaved-value="any"
主主要用在主鍵屬性不是通過Hibernate生成,而是程序自己setId()的時候。
在這里多說一句,強烈建議使用Hibernate的id generator,或者你可以自己擴展Hibernate的id generator,特別注意不要使用有實際含義的字段當做主鍵來用!例如用戶類User,很多人喜歡用用戶登陸名稱做為主鍵,這是一個很不好的習慣,當用戶類和其他實體類有關(guān)聯(lián)關(guān)系的時候,萬一你需要修改用戶登陸名稱,一改就需要改好幾張表中的數(shù)據(jù)。偶合性太高,而如果你使用無業(yè)務(wù)意義的id generator,那么修改用戶名稱,就只修改user表就行了。
由這個問題引申出來,如果你嚴格按照這個原則來設(shè)計數(shù)據(jù)庫,那么你基本上是用不到手工來setId()的,你用Hibernate的id generator就OK了。因此你也不需要了解當
unsaved-value="none"和
unsaved-value="any"
究竟有什么含義了。如果你非要用assigned不可,那么繼續(xù)解釋一下:
unsaved-value="none" 的時候,由于不論主鍵屬性為任何值,都不可能為none,因此Hibernate總是對child對象發(fā)送update(child)
unsaved-value="any" 的時候,由于不論主鍵屬性為任何值,都肯定為any,因此Hibernate總是對child對象發(fā)送save(child)
大多數(shù)情況下,你可以避免使用assigned,只有當你使用復(fù)合主鍵的時候不得不手工setId(),這時候需要你自己考慮究竟怎么設(shè)置unsaved-value了,根據(jù)你自己的需要來定。
BTW: Gavin King強烈不建議使用composite-id,強烈建議使用UserType。
因此,如果你在系統(tǒng)設(shè)計的時候,遵循如下原則:
1、使用Hibernate的id generator來生成無業(yè)務(wù)意義的主鍵,不使用有業(yè)務(wù)含義的字段做主鍵,不使用assigned。
2、使用對象類型(String/Integer/Long/...)來做主鍵,而不使用基礎(chǔ)類型(int/long/...)做主鍵
3、不使用composite-id來處理復(fù)合主鍵的情況,而使用UserType來處理該種情況。
那么你永遠用的是unsaved-value="null" ,不可能用到any/none/..了。
posted @
2009-08-17 22:41 J2EE學習筆記 閱讀(943) |
評論 (0) |
編輯 收藏