-源於我對它的一個誤會
sparta-紫杉 2010-4-14 9:02 java
開發環境: eclipse3.4.2 + weblogic10.3 + jdk1.6.0_13 + Struts2.1.8 + Spring3.0.1 + Hiberante3.3.2程序員
初識Hibernate3的查詢結果返回Map的功能是很是高興的,由於我第一印象認爲它可以解決相似如下的問題:web
從角色表SysRoles中,獲得以Role_Id(角色ID)爲Key, 以Role_Desc(角色描述)爲Value的Map。數據庫
要知道在之前,我一般採用以下的傳統方式解決此類問題:eclipse
public HashMap getRolesMap() {
try {
List<SysRoles> results = getHibernateTemplate().find("from SysRoles");
Map hashMap = new HashMap(0);
for( SysRoles role : results ){
hashMap.put( role.getRoleId(), role.getRoleDesc());
}
return hashMap;
} catch (RuntimeException re) {
throw re;
}
}jsp
代碼雖也很少,短短5行代碼,但在代碼裏面須要通過一次提取,一次加工的過程也有點繁瑣,程序員嘛,要本着一個「代碼簡潔、清晰」的原則。this
有無更好的方法呢?那麼在知道Hibernate3可以從查詢結果中返回Map的功能後,我很天然的寫下以下的代碼,覺得能節省一個加工的過程:spa
public HashMap getPermissionMap() {
try {
HashMap hashMap = (HashMap)getSession()
.createSQLQuery("select role_Id, role_Desc from Sys_Roles").setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
return hashMap;
} catch (RuntimeException re) {
throw re;
}
}.net
哈哈,只有兩行代碼便能解決這種問題,看起來彷佛是理想的選擇,趕快試試吧。hibernate
運行後,在jsp偌大的空白頁面上,一行異常代碼分外刺眼: org.hibernate.impl.SQLQueryImpl cannot be cast to java.util.HashMap.
看來setResultTransformer是不能直接返回Map的,它是返回一個封裝了全部Map的List。
看來我還真是太草率了,對上面語句中的「Transformers.ALIAS_TO_ENTITY_MAP」迷感了雙眼。
看來事情並不那麼簡單,還須要探索,既然已經開始了,索性探索個明白吧!在通過對代碼、對查詢方法進行幾回修訂後,成爲以下代碼:
public HashMap getPermissionMap() {
try {
List list = getHibernateTemplate()
.find("select new Map(sysRoles.roleId as roleId, sysRoles.roleDesc as roleDesc) from SysRoles sysRoles");
for( Object map : list){
hashMap.putAll( (Map) map );
}
return (HashMap) hashMap;
} catch (RuntimeException re) {
throw re;
}
}
修改完成以後,我不由啞然失笑,以前堅持的所謂的「代碼簡潔、清晰」原則已經蕩然無存。但反觀代碼,彷佛有些道理,同時,對java的使用也有了更進一步的理解,對浪費的時光也再也不那麼憐惜了。
惋惜事情總不能如你所願,經過對代碼的執行發現,數據庫裏面有多條數據,可是那個返回的hashMap的size卻始終是一條。
我經過下面代碼對返回的hashMap進行循環打印,發現僅能打印一條,而且內容始終是同樣:
for( Map.Entry map : hashMap.entrySet() ) {
System.out.println("#### map1.getKey() = " + map1.getKey() + ", map1.getValue()=" + map1.getValue());
}
唉,仍是找找相關的「從Hibernate的查詢結果中返回Map」的原理吧,看來咱們多少得爲咱們的草率付出點代價。
下面是我從Hibernate的教程中獲得的相關信息,一個說得比較清楚的是javaEye(如今爲itEye)的蔡華江,請原諒使用了你的文章:
//////////////////////////////////////////////////// 摘抄開始 ///////////////////////////////////////////
在問答裏和論壇中,常常看到有人問,怎樣將使用本地SQL查詢出來的結果映射爲值對象的問題,這裏就Hibernate中提供的方法作個結論。
前提,這裏沒有使用屬性的延遲加載技術。
假設有個值對像,以下:
package test;
public class Person {
private Long id;
private String name;
private Long age;
private Long phone;
private String address;
public Person(Long id, String name, Long age, Long phone, String address) {
this.id = id;
this.name = name;
this.age = age;
this.phone = phone;
this.address = address;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getAge() {
return age;
}
public void setAge(Long age) {
this.age = age;
}
public Long getPhone() {
return phone;
}
public void setPhone(Long phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
若是查詢所有五列記錄的話,那麼只要
List list = getHibernateTemplate().loadAll(Person. class);
若是隻須要查詢id,name,age三列記錄呢?那麼就要新增構造方法了,
public Person(Long id, String name, Long age) {
this.id = id;
this.name = name;
this.age = age;
}
而後呢,就能夠經過HQL來進行查詢。
List list = getHibernateTemplate().find("select new test.Person(id,name,age) from Person");
這個方法一般能夠知足須要了,只是若是,只須要查詢id,name,phone三列記錄的話,還新增構造方法?不行了,會出現構造方法衝突了。有個辦法:
List list = getSession().createQuery("select id,name,phone from person")
.addScalar("id",Hibernate.Long).addScalar("name").addScalar("phone",Hibernate.Long)
.addEntity(Person. class);
可是,這個只適用於存在Person實體的,若是Hibernate中沒有進行Person映射的呢,系統中只存在一個JavaBean。
List list = getSession().createSQLQuery("select id \"id\",name \"name\",phone \"phone\" from person")
.addScalar("id",Hibernate.Long).addScalar("name").addScalar("phone",Hibernate.Long)
.setResultTransformer(Transformers.aliasToBean(Person. class)));
那麼Hibernate就會自動將查出來的三列內容組裝到VO對象中去,只是代碼量有點大,並且名稱都須要從新定義爲小寫的,在Oracle中查出來的列都默認爲大寫的(不知道其它數據庫怎麼樣),這個辦法就不依賴於構造方法了,只須要定義私有變量,設置getter/setter方法就好了。
不過若是更猛點的,根本就沒有JavaBean對象能夠填充怎麼辦,Hibernate能夠將查出來的列組裝到集合類中去。如Map。
List list = getSession().createSQLQuery("select * from person")
.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//sparta-紫杉 注:這個ALIAS_TO_ENTITY_MAP的含義是指以數據庫裏的字段爲key,以字段內容爲Value的Map。有幾個字段就會生成幾個Map,而後將全部的Map組裝到List中。
除了這個Transformers還能夠將列轉化爲List。
List list = getSession().createSQLQuery("select * from person")
.setResultTransformer(Transformers.T0_LIST);
到此,還能夠經過繼承Transformers將結果映射爲其它對象,不累述了,基本功可以用了。
//////////////////////////////////////////////////// 摘抄結束 ///////////////////////////////////////////
哈哈,各位達人,看完上面這篇文章以後,豁然開朗了吧?
請注意在上面文章中筆者註釋的一段文字就會明白:在hibernate從查詢結果返回Map中,是以字段爲key,以字段的內容爲Value的Map,而不是我想象的假設我在查詢中僅提供兩個字段,那麼Hibernate就會天然地以個人第一個字段的內容爲key,以第二個字段的內容爲Value生成Map列表。
那麼我也終於明白爲何在個人執行結果中僅有一條數據的緣由了,由於在個人代碼中返回的list裏面,雖然有不少條數據,可是咱們應該明白一點:
當我使用Map的putAll時,因爲在Map中,均是以字段名稱做爲key的,那麼在數據庫中不管有多少條的記錄,字段名始終是相同的,也就是說,在Map中重複的key會被反覆覆蓋,僅保留最後的一條。 它是返回一個封裝了全部Map的List,在使用時,只有迭代該List,提取各Map進行使用纔不會致使重複覆蓋。
什麼樣的應用場景賦予咱們什麼樣的想象力,誤會有時源於無知,但更多源於咱們的無限想象力,有時異想天開的靈感確實能爲咱們提供打開另外一扇未知知識大門的鑰匙。 難道不是嗎? 本次誤會讓我對Hibernate的理解又深刻了一層,不管是原理仍是應用場景。再者,不妨將牛吹大一點吧:但願Hibernate團隊可以從個人這個誤會中吸收點靈感,開發出一個可以返回之前後兩個字段的內容分別爲key和value的Map的方法來,以方便各位使用Hibernate的程序達人,彷佛也何嘗不可吧? 哈哈!