最后一种方式就是声明控制的事务处理了,这里面基本上都是在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的原因,通过上面的方法排出了这个猜测。