
最后一種方式就是聲明控制的事務處理了,這里面基本上都是在xml文件中配置,在代碼中見不到任何有關事務的類型,實現了非侵入。其原理用到了Ioc對象反轉控制和AOP,到現在我還沒弄清楚Aop是怎么個玩意兒,哈哈,還得好好學。
在spring的配置文件中設置一個代理類對象,其中的屬性包括,要代理類的接口,要代理的哪個類,事務的處理方式,這樣在數據實現類中調用數據庫的方法中就像沒有用事務一樣寫代碼,另外在控制器中調用這個處理方法的時候,以前是要通過Ioc獲得一個數據實現類的對象,現在,這個實現類被代理的,我們只需得到這個代理類的對象,然后用這個代理類的對象調用相應的數據處理方法。
Xml配置文件中的部分配置:
<bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>springmvcwebapp.UserLoginInterface</value>
</list>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<ref bean="oneUserLoginImple"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="doUpdateUserInfo*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED, readOnly</prop>
</props>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</property>
<property name="url">
<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=WebStudyDB</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value>123456</value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
用代碼控制實現事物管理的另一種方式,就是通過使用TransactionTemplate模板類,TransactionTemplate封裝了事務管理的功能,包括異常時的事務回滾,以及操作成功后的事務提交。和JdbcTemplate一樣,它使得我們無需在瑣碎的try/catch/finally代碼中徘徊---也就是為我們省去了部分事務提交、回滾代碼。
將上面的用戶信息插入方法修改成下面這樣:
TransactionTemplate類的構造器需要一個TransactionCallback接口類型的參數,而抽象類TransactionCallbackWithoutResult實現了TransactionCallback接口,抽象類TransactionCallbackWithoutResult中有一個TransactionCallbackWithoutResult()方法,該方法以TransactionStatus 為參數。所以就有了下面的用內部類的實現方式。
public boolean doUpdateUserInfo(UserInfoVO oneUserInfo)
{
//定義兩個sql插入語句
String sql = "update userinfo set userPassword=? where userName=?";
String sql2 = "update userinfo1 set userPassword=? where userName=?";
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult()
{
public void doInTransactionWithoutResult(TransactionStatus status)
{
this.jdbcTemplate.update(sql, new Object[]
{oneUserInfo.getUserName(),
oneUserInfo.getUserPassword()});
this.jdbcTemplate.update(sql2, new Object[]
{oneUserInfo.getUserName(),
oneUserInfo.getUserPassword()});
}
});
return true;
}
這種方式,代碼量少,少了try/catch/finally語句,全部被封裝在了TransactionTemplate類里面,以內部類方式實現可能降低了代碼的可讀性。
哈哈,有人在我的blog上留言說也在學習spring,能不能傳個例子共同學習一下spring的事物管理,我也是學了個一知半解,剛剛研究了研究。將其中的一點自己的理解寫下來,共同學習。
Spring的事物管理有兩種,一種是代碼控制事物,一種是聲明控制實現事物管理(也就是xml配置),咱先來說用代碼控制實現事物管理,這種方式又分為兩類,一種是實現PlatformTransactionManager接口的方法
查了一下幫助文檔,實現了PlatformTransactionManager接口的有好多類,下面的例子代碼是用的其中的DataSourceTransactionManager.
這是模擬用戶注冊的一個例子,下面的類實現于自定義接口UserLoginInterface(spring提倡的面向)該類里面定義了兩個屬性,一個是Spring的jdbc模版類,一個是事務管理器,并都提供了set方法,這兩個對象將在Ioc中自動初始化加載。其中有一個叫UserInfoVO的值對象類,用來傳遞用戶注冊信息
//用代碼控制實現事務管理其中的實現PlatformTransactionManager接口的方法
import java.util.*;
import org.springframework.context.*;
import org.springframework.jdbc.core.*;
import org.springframework.transaction.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.DefaultTransactionDefinition;
public class UserLoginImple implements UserLoginInterface
{
//Spring jdbc模版類
private JdbcTemplate jdbcTemplate;
//事務管理器 PlatformTransactionManager是一個接口
private PlatformTransactionManager transactionManager
//jdbc模版類對象的set方法
public void setJdbcTemplate(JdbcTemplate jdbcTemplate)
{
this.jdbcTemplate = jdbcTemplate;
}
//事務管理器的set方法
public void setTransactionManager(PlatformTransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
//插入用戶信息的方法
public boolean doUpdateUserInfo(UserInfoVO oneUserInfo)
{
//定義兩個sql插入語句
String sql = "update userinfo set userPassword=? where userName=?";
String sql2 = "update userinfo1 set userPassword=? where userName=?";
DefaultTransactionDefinition dtf = new DefaultTransactionDefinition();//事務定義
TransactionStatus status = this.transactionManager.getTransaction(dtf);//事務狀態
try
{
this.jdbcTemplate.update(sql, new Object[]
{oneUserInfo.getUserName(),
oneUserInfo.getUserPassword()});
this.jdbcTemplate.update(sql2, new Object[]
{oneUserInfo.getUserName(),
oneUserInfo.getUserPassword()});
this.transactionManager.commit(status);
}
catch(Exception ex)
{
this.transactionManager.rollback(status);
return false;
}
return true;
}
}
關鍵看加粗的部分,首先定義一個事務,然后以這個事務為參數通過事務管理器創建一個事務狀態,開始執行sql代碼,最后事務管理器提交事務狀態,完成。其間如果出現異常,事務管理器回滾事務狀態。
我理解著,這里面用了設計模式中的一個叫Command命令的模式,首先定義一個執行者(事務對象),然后賦予執行者使命(事務狀態),命令執行者執行其被賦予的使命,如果失敗,命令執行者撤銷執行的使命。
不知道理解的對不對,呵呵。
看編程思想的異常處理這一章,Throwable是所有異常的基類,Error和Exception分別繼承了Throwable,Error是虛擬機運行報告錯誤,一般我們不用關心,Exception 是所有編程異常的基類,它又分為被檢查異常和不檢查異常,不檢查異常是所有繼承于RuntimeException的異常,這些異常編譯器在編譯時不進行檢查,也就是不用有異常聲明,遇到錯誤會自動拋出,被檢查異常時除RuntimeException的異常,必須有異常聲明,編程思想里面的說法好像大家對強制異常聲明不是很贊同,理由沒能理解深刻,都是大師們說的話,哈哈。咱還不夠那個級別。
其中有這樣一個問題,就是一個方法有一個異常必須作處理,可又不知道該如何處理,這里有個辦法就是在catch里面將其轉換成運行時異常RuntimeException,這樣就不用異常聲明了。
哈哈,剛剛看了異常處理這一章,今天就用上了,編好了一段hibernate的代碼,測試就是沒有結果輸出,并且沒有異常出現,真是讓人惱火,找阿找阿找,最后發現在初始化SessionFactory的靜態語句塊中雖然用了try…catch語句但是在catch里面只是將異常拋出,而在跳用這段代碼的時候,并沒有獲取這個異常,所以造成異常丟失。
另外,錯誤的原因是虛擬機找不到一個屬性的get方法,換了個名字就好了,注意命名規則,一個小寫字母然后接一個大寫字母就不行,像這樣sLive。注意/。
在執行Hibernate程序中出現下面錯誤
Could not read mappings from resource: DepartmentTwo.hbm.xml
檢查也沒錯阿,找不到的文件老老實實在那呆著了阿,仔細檢查異常信息,發現下面還有這么一句。
org.hibernate.DuplicateMappingException: Duplicate class/entity mapping com.hwebmapping.pojo.Department
原來一個持久類不能多次被映射,所以才出現這樣的異常,原因是在做練習的時候為了省事,將原來做好的持久類想接著用一下,沒想到Hibernate有意見,哈哈。
如果在調用session.save()方法的時候如果傳入的對象所的類沒有被映射,就會出現線面的異常信息。
uninitialized proxy passed to save()
未初始化的代理
不錯,今天收獲不小,哈哈。
轉眼間跨到07年了,呵呵。
今天學習了Hibernate的List,Set,Map映射,它們都是應用類之間的內嵌方式,List和Set都是按數值索引,Set不能有重復值,List必須有一個Index字段,和主健形成聯合主健,Map是以鍵值對形式的集合,也不允許重復,用那種方式取決于數據庫表如何設計,和一對多不同的是,一對多采用關聯方式,具體體現在持久類里面倒沒看出什么差別,在配置文件上有差別,在實際應用中體會吧。
開始有就業的壓力了。
轉眼,幾個月過去了,要找工作了,有些郁悶,沒有底氣阿,為什么?沒好好學么?覺得還可以,呵呵,不知道人家是什么胃口,娘的。
花了這么多錢,不知道值不值,想想,我們這些參加什么培訓的,都是些急于求成的東西,跑這來想速成,呵呵,天下沒有什么速成,我要多長時間把這錢掙回來?半年?要是凈賺還行,呵呵,不了解外界什么行情,想想,人家因為什么理由要我呢?呵呵,要學的還很多,也是,總不能等萬事俱備再出山不是,那就出不了山了,怎么現在還沒有在學校找工作那時候有自信了呢?那時候可能我還比周圍人還有一些競爭力吧,現在?不知道自己在什么層面上,不知道自己有什么優勢了。怎么越過越倒退呢,一晚上心煩,狗日的。
要元旦了,呵呵,日子怎么是這樣的呢,我討厭過節,昔日的好兄弟說我小子越來越孤僻了,得好好教育教育了,唉,確實如此,我倒想,沒有人認識我,沒有人找我,來無影去無蹤,呵呵,不是我不想和人來來往往,熱熱氣氣,就像羅桑實說的,暖暖的都不想離開了。可是。今年下半年,我好想誤入了一個大口袋一樣的包圍圈,進去了,口袋被慢慢扎緊,無法呼吸,我在努力掙脫,想出去,可是,又不想互相殘殺,不想弄壞袋子,想和解,我還抱有希望,呵呵,希望有一天,我殺出重圍。
討厭過節。
剛剛搞定一個從昨天就搞不定的問題,都已經瘋了,鬧了半天,該了個名字就好了,我要做一個一對多的映射,一個出版社對應多本書,在Publish類里面定義了一個Set的eBook,就是這個eBook這個名字,換了個叫someBook就好了,想想,我的書類叫EBook,難道和這個有關系?納了悶了,總是報Could not find getter for eBook in Publish類。
關于Hibernate的延時加載,如果出現
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.hwebmapping.pojo.Person.cats, no session or session was closed
的異常,就是Hibernate要讀取數據的時候,Session已經關閉,可以用監聽器的方法來在頁面加載完成后關閉session.(implements javax.servlet.Filter).
出現下面這樣的錯誤:
java.lang.NoClassDefFoundError
at com.hwebmapping.hibernate.HibernateDAOBean.doInsertCardDataToDB(HibernateDAOBean.java:337)
337行是下面finally中的語句
finally
{
HibernateUtil.closeSession();
}
經查原來是因為,在Card.hbm.xml中的類名寫錯了,找不到,但是報上面的錯誤讓人有點摸不著頭腦了,為什么是在closeSession的時候出現的異常呢,按理說應該在加載xml的時候就應該有異常了阿。費解。
<hibernate-mapping>
<class name="com.hwebmapping.pojo.Card" table="Card">
Hibernate第二天,沒有抽出時間來寫日志,呵呵,這兩天過的有些沒有規律。
涉及到Hibenate的核心api,SessionFactory,Session,Query,Transaction。剛開始搞得有點暈頭轉向,呵呵,hibernate比spring復雜,的確不用寫什么sql語句,配配配還是配,就這配,非得仔細不可,就因為一個字母寫錯,就得找半天,報的錯還聲東擊西。呵呵。
昨晚上在網上閑逛,看到了一篇文章,忘了記下來網址了,說學java的三個境界,哈哈,同志尚需努力啊。
------------------------------------------------
第一個層次:精通掌握Java語法、能調試基本的程序錯誤,精通掌握JSP+Java Bean寫一些N年前ASP、PHP翻版的Java Web應用程序(如論壇、網站新聞發布系統、OA、網上商城等),精通JDBC使用、精通SQL語句、精通XML等。
第二個層次:掌握設計模式原理及應用,掌握基于OO的分析及設計方法,并能精通熟練使用幾種Java專業設計及開發工具,精通掌握流行的J2EE框架如Hibernate、EJB、Webwork、Spring的原理及應用,精通J2EE中一兩個組成部分(如Servlet、EJB等)的工作原理及細節。
第三個層次:少林的高僧有兩種,禪僧及武僧。J2EE程序員的第三個層次也同樣有禪、武兩個分支,這里我們重點分析一下:
第一個分支屬于走的禪僧線路。在練完第二個層次中的各種武功基礎上,結合實際項目中的千奇百怪的用戶需求,游刃有余的選擇適合的技術方案為客戶解決問題,并形成自己的一套解決方案。達到這一個層次的J2EE程序員已經不在乎使用任何工具、任何框架了,而是根據不同的對手,使用不同的武器或招式來應對。好比小李飛刀一樣,只有達到了“手中無刀、心中有刀”的境界,才能達到“出手一刀,例不虛發”的效果。這一層次的武功屬于一個熟練度問題,刀練得多了、遇到的對手多了,再加上前面的武功修為,就算做不到例不虛發,也可達到十發九中。
第二個分支屬于走的武僧線路,在撐握熟悉第一二個層次中涉及到的內容后,進一步專研并撐握J2EE底層開發,J2EE規范制訂、規范實現、Java虛擬機的工作原理、各種常見的J2EE服務器內核工作機制、內存管理、進程機制、源代碼等。因為涉及的很多東西都比較抽象,代碼也很多,練這一層的武功需要有很好的資質及耐性、并具還得有一定的環境及條件。好比神雕大俠楊過拿起“玄鐵劍”,并練成“暗然銷魂掌”的成長過程,需要前面的武功修為作基礎,更需那只威力神武神雕的幫助指點及他處處為民、懲奸除惡的俠之心態。
胡侃了這么多,現在來根據自己情況測算一下自己的份量,結果如下:
第一層 練到8成;
第二層 練到5成;
第三層 準備走禪僧線路,當前算是練到1成;
唉,后面的武功提升越來越難,真不知道要到何年何月才能達到10成啊。你的武功練到哪一個層次了,不防亮出來大家切磋切磋。嘿嘿,要是有一天,咱們中國的Java程序員人手一把“玄鐵劍”、人人會使“暗然銷魂掌”,那還了得!汗...,寫著寫著居然做起白日夢了,不好意思,就此打住。
今天開始講Hibernate了,搞得有點暈,其中涉及到一些線程并發的只是,還有集合,真是應了先前基礎課老師說的,以后學web開發很多地方要用到集合,多線程雖然框架已經做了封裝,但是要想很好的理解它們的運行機理,還是要明白的,好好研究編程思想去。
還有就是今天看到了一個用到靜態語句塊的地方。在HibernateUtil類里,用來初始化SessionFactory.
對Hibernate的初步理解,它通過配置xml將持久化類與數據庫的一條記錄連接了起來。Hibernate也是輕量型框架,所謂輕量型,必須有兩個特點,一是非侵入性,而是與容器無關性(不依賴于容器,也就是可在J2EE中運行,也可以在J2SE中運行)。Hibernate對JDBC做了很高的封裝,讓我們看不到任何涉及到數據庫的代碼,完全面向對象編程。
第一天,還得好好學。
今天講spring的DAO組件的事務處理和spring和struts結合的三種方法。
Jdbc的事務處理分為全局事務和局部事務,全局事務是基于應用服務器實現的事務,局部事務是程序實現的,Spring的事務處理是局部事務。
Spring 的事務處理又分為代碼控制和聲明控制,代碼控制又分為實現PlatformTransactionManager接口方式和繼承TransactionTemplate方式(需要用到匿名內部類,這里涉及到了一個回調的概念,回去研究研究編程思想,以前看到過沒仔細看,好像回調只能用內部類實現,在java中)。
Spring 與Struts 結合有三種方法,一種讓Action繼承一個ActionSupport類,這個類是由Spring 提供的,這是最傻瓜的方法,我覺得,呵呵,然后就是替代Struts中的RequestProcessor類,Spring在替代類里面做了些手腳,使Action類能夠基于IoC容器來動態加載業務類對象,第三種方法,是在struts-config.xml中,將所有指定Action的Path屬性設成spring的一個接口,然后在spring配置文件中配置action類。這是推薦的方法。
一天下來頭疼,明天開始講Hibernate了,這樣struts+spring+hibernate架構就學完了,要學的東西真是太多了,都跟不上新版本出來的速度,呵呵,要開始著手找工作了,郁悶,自己水平不夠好。
今天接著spring的SimpleFormController,使用spring框架如何提交表單,處理表單。
同Struts相似之處的也是在web.xml中設置一個servlet控制器,然后有個xml文件來配置業務類,感覺比Struts還簡單些,呵呵,都是做的簡單的例子,還后涉及到事件,在發生某個響應后可以出發一個事件處理另一個并發的業務,比如用戶注冊成功后給用戶郵箱發送一封郵件,這需要用到spring的兩個類,一個ApplicationListener監聽器,還有個事件類ApplicationEvent,一個implement 一個extends,ApplicationEvent是一個抽象類,必須實現它,在事件觸發類中創建一個ApplicationEvent實現類的對象,構造器中以ApplicationListener的實現類的對象為參數,然后將ApplicationEvent實現類的對象作為參數調用ApplicationContext的publishEvent();方法。
最后,簡單介紹了spring的dao組件,經過封裝后的jdbc確實好用,本來十幾行的代碼兩行就搞定了。其中spring的dao組件不依賴于ioc,但是也可以用ioc注入,還有事務處理,還沒有學到,這兩天作bbs的例子。



試一試baidu的上傳圖片,還不錯,挺方便。
試著實現了一下Struts和spring結合,在Struts的Action類中,使用spring得到業務類對象,結果開始啟動Tomcat的時候報下面這個錯誤:
嚴重: Error listenerStart
2006-12-21 16:32:49 org.apache.catalina.core.StandardContext start
嚴重: Context [/BookShop] startup failed due to previous errors
其中在web.xml中配置spring的啟動方式,是用的監聽器模式:
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
在網上查了一下相關帖子,說把監聽器模式換成servlet啟動模式就沒有問題了,但是是為什么還不知道,按照此說法把spring的啟動方式在web.xml中配置成了servlet啟動模式。
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
結果就好了,更加奇怪的是在設回成監聽器,也沒有問題了,我暈。
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
。。。。。。。。
今天來了個老師講一些人生規劃、職業規劃、工作、面試。講的我有些郁悶,大道理誰都懂,但是…,哈哈,但是就很多了,要不誰都成偉人了呵呵,不是我消極,這些問題都是沒有確切說法的,不想編程,true就是true,false就是false,這么寫就能通過,那么寫就是編不過,人活著,有很多可以選擇,也可以不選擇,這或許是為什么人活著很辛苦,呵呵,關于什么以誠待人啊什么的,誰都知道,誰都有一個評判,但是,做與不做,是不一樣的人與人之間,都知道,付出就有回報,這都懂,甚至還做過試驗,呵呵,可是,現在,我好像,沒資格說這些了。我不知道還能不能回來。
因為在Struts中配置一個數據庫連接池,折騰了一晚上,按照書上寫的就是出問題,就在網上搜啊搜,還真是不好找,倒是有很多提出這樣問題的帖子,但是都沒回答到點子上,結果還是在網上找到了一篇文章,討論是用Tomcat的連接池好還是用Struts的連接池好,結論是用哪個也不好,哈哈,其中有一段在Struts中配置連接池的實例,正好對我這個癥狀,哈哈,問題解決,甚是高興。
下面是摘自這篇文章關于配置Struts數據源的內容:
Struts DataSource管理器在Struts配置文件(Struts-config.xml)里定義。這個管理器可以用來分發和配置任何實現了javax.sql.DataSource接口的數據庫連接池(connection pool)。如果你的DBMS或者容器內置了符合這些要求的連接池,你可以優先選用它。
Jakarta的公共連接池實現 - BasicDataSource
如果你的手頭沒有連接池的本地(native)實現,你可以使用Jakarta提供的公共連接池實現[org.apache.commons.dbcp.BasicDataSource],它可以和DataSource管理器"合作"的很好。另外,Struts還在它的util包里包含了一個GenericDataSource類,這也是一個連接池實現。但是這只是一個非常簡單的實現方案,不推薦使用,因為它可能在Struts的以后版本中被BasicDataSource或其它的數據源實現替換掉。
下面是一段Struts-config.xml配置文件中的數據源配置(使用GenericDataSource數據源實現),你可以更改相應的設置以適合你自己的系統。
<!-- configuration for GenericDataSource wrapper -->
<set-property="autoCommit" value="false"/>
<set-property="description" value="Example Data Source Configuration"/>
<set-property="driverClass" value="org.postgresql.Driver"/>
<set-property="maxCount" value="4"/>
<set-property="minCount" value="2"/>
<set-property="password" value="mypassword"/>
<set-property="url" value="jdbc:postgresql://localhost/mydatabase"/>
<set-property="user" value="myusername"/>
使用BasicDataSource數據源實現的配置方案如下:
<!-- configuration for commons BasicDataSource -->
<set-property="driverClassName" value="org.postgresql.Driver" />
<set-property="url" value="jdbc:postgresql://localhost/mydatabase" />
<set-property="username" value="me" />
<set-property="password" value="test" />
<set-property="maxActive" value="10" />
<set-property="maxWait" value="5000" />
<set-property="defaultAutoCommit" value="false" />
<set-property="defaultReadOnly" value="false" />
<set-property="validationQuery" value="select COUNT(*) FROM market" />
出處:http://wenson.javaeye.com/blog/33316
值得注意的是上面兩個數據源的配置中的property屬性的值有些不一樣,就是這個問題困擾了我一晚上。漲經驗了呵呵,以后也很可能不會用Struts配置連接池。
另外,還查得了如何查看sqlserver2000的版本的方法,如下:
SELECT @@VERSION
SQL Server 2000 版本和級別 @@VERSION 產品級別
SQL Server 2000 原始版本 8.00.194 RTM
Database Components SP1 8.00.384 SP1
Database Components SP2 8.00.534 SP2
Database Components SP3、SP3a 或 MSDE 2000 Release A 8.00.760 SP3
Database Components SP4 8.00.2039 SP4
原來以為是數據庫沒有裝sp3的原因,通過上面的方法排出了這個猜測。