SSH开发新方案之基于SSH开发架构的重新分层.docx_第1页
SSH开发新方案之基于SSH开发架构的重新分层.docx_第2页
SSH开发新方案之基于SSH开发架构的重新分层.docx_第3页
SSH开发新方案之基于SSH开发架构的重新分层.docx_第4页
SSH开发新方案之基于SSH开发架构的重新分层.docx_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ssh开发新方案之基于ssh开发架构的重新分层现代的企业开发中,越来越多地引入了多层架构设计模式。struts+spring+hibernate (一下简称为ssh)就是其中之一,ssh架构是当前非常火的架构,很多金融、电信项目,大型门户网站均选择该架构作为业务支撑架构,开发流程也已经非常成熟。但是该结构开发起来,依旧存在一些问题。分析这些问题,得先从ssh架构的组成说起。ssh为struts+spring+hibernate的组成方式,struts实现mvc,spring负责架构的结合,hibernate进行数据的持久化。通常其分层开发的结构图(以一个业务新增为例)如下:这样的结构,满足了一般的业务需要,但是对于当前日益复杂化的web2.0的开发,却存在不少问题,归纳起来主要有以下几点的不足:a)dao和服务层容易出现职责不明,由于按照mvc逻辑,业务代码应该写在struts action里,但是其事务的提供,却是配置在service层。为了一组在逻辑上完整的数据操作业务逻辑,需要涉及两个层(serveice、action)来进行编写,遇到判断的情况下,为了保证完整的事务操作,则需要将业务代码移到service层完成,而通常习惯了在struts action里调用多次service而产生多个事务而在出现exception时导致出错时操作之前调用的service事务的业务数据没有回滚。b)当需要返回的数据供ajax使用,操作json或xml的的大量使用时。开发起来会很费力,一段同样的业务代码,为了使用ajax和xml可能需要重新编写一次,或者在同一个action里通过标志来判断,对分层结构造成了比较糟糕的破坏。如果设计得不好,为了使用json和xml还得额外增加大量的配置,严重降低了开发效率。因此,为了克服这些缺点,本人对于ssh架构,进行了实现了重新的分层,共享了业务代码。简化了开发、增强了与ajax技术、mxl技术的结合。提供了一种更高效的开发模式。其开发的结构图如下:看到这个架构图有人可能会问,struts action类的编写去哪了呢?答案正是这个架构的优点,由于业务代码统一实现ibusinessservice接口,使得只需要相对固定的几个struts action类调用service层的方法,便可以完成工作。包括json格式输出,xml输出及webservice输出均调用service层方法来完成功能。这样便实现了业务代码的分离,以及与前端框架的极大解耦。结合spring+hibernate与jdbc的事务问题背景:我们是一家工作流公司,客户采购我们的产品后,将其嵌入其项目中。我们的工作流采用的是 spring+hibernate的方式,客户项目则是jdbc直接进行数据库操作。问题:客户在其数据库操作过程中需要调用我们的工作流接口,这样就需要将我们的工作流操作与他们的业 务操作置于同一个事务中。我们的服务采用的都是spring的声明式事务,而客户采用的是对 connection进行事务处理。如何保证事务的一致性?想到的解决方案一:使用jta事务,用tomcat+jotm提供事务管理器。为什么一开始就想到要使用jta事务?实际上我们和客户都是使用的同一个数据库,为了方便,各自使用了不同的数据库连接方式,使用jta的话确实有bt的意思在里面。但是事实上是我们的第一反应都是jta。最后没有采用该方法的原因也很简单:我没有将jotm配置成功!汗一个。想到的解决方案二:将客户的这些特定代码用spring管理起来。因为要修改客户部分代码,这个方案遭到了客户的强烈反对。于是放弃。想到的解决方案三:客户数据库操作与我们的服务使用同一个数据库连接。然后编程处理事务。存在两种方式:一种是把客户的连接传给我们,另一种则是把我们的连接传给客户。第一种方式对我们的影响太大,所以最后决定采用后一种方式:从hibernate session中获取connection然后传递给客户。接下来查看一下hibernatetemplate的execute()方法,思路就很简单了:获取定义的sessionfactory-创建一个新的session并打开-将session与当前线程绑定-给客户代码返回connection-打开事务-客户使用我们传递的connection进行数据库操作-我们不带声明事务的服务操作-提交事务-解除绑定。实际要注意的地方是:1、将session与当前线程绑定使用的transactionsynchronizationmanager.bindresource()方法,这样在hibernatetemplate里才能找到session; 2、我们的服务一定要把声明式事务彻底干掉,否则会有commit; 3、我们服务调用完毕后一定要flush session,否则客户代码不会感知数据库里的数据变化。最终解决:使用了spring里常用的模板和回调。代码如下:publicclasstransactiontemplateprotectedfinalloglogger=logfactory.getlog(transactiontemplate.class);privateflushmodeflushmode=flushmode.always;publicobjectexecute(transactioncallbackcallback)/首先获取sessionfactorysessionfactorysessionfactory=(sessionfactory)framework.getengine().getcontainer().getcomponent(sessionfactory);/创建一个新的session并打开logger.debug(openingsinglehibernatesessionintransactiontemplate);sessionsession=getsession(sessionfactory);/将session与当前线程绑定transactionsynchronizationmanager.bindresource(sessionfactory,newsessionholder(session);/获取数据库连接connectionconn=session.connection();objectresult=null;transactiontransaction=null;try/开始处理事务transaction=session.begintransaction();tryresult=callback.dointransaction(conn);catch(runtimeexceptionex)dorollback(session,transaction);throwex;catch(errorerr)dorollback(session,transaction);throwerr;/如果数据库操作过程中没有发生异常则提交事务mit();catch(workflowexceptione)logger.error(数据库操作失败,事务回滚也失败!);throwe;catch(runtimeexceptionex)logger.error(数据库操作失败,事务被回滚!);throwex;catch(errorerr)logger.error(数据库操作失败,事务被回滚!);throwerr;finally/将session与当前线程解除绑定transactionsynchronizationmanager.unbindresource(sessionfactory);doclose(session);returnresult;protectedsessiongetsession(sessionfactorysessionfactory)sessionsession=sessionfactoryutils.getsession(sessionfactory,true);flushmodeflushmode=getflushmode();if(flushmode!=null)session.setflushmode(flushmode);returnsession;privatevoiddorollback(sessionsession,transactiontransaction)logger.debug(数据库操作异常,开始回滚事务);trytransaction.rollback();logger.debug(回滚事务成功!);catch(exceptione)logger.error(回滚事务失败!);thrownewworkflowexception(回滚事务失败!);finallysession.clear();privatevoiddoclose(sessionsession)logger.debug(开始关闭连接);trysession.close();catch(exceptione)logger.error(关闭连接失败!);thrownewworkflowexception(关闭连接失败!);publicflushmodegetflushmode()returnflushmode;publicvoidsetflushmode(flushmodeflushmode)this.flushmode=flushmode;publicinterfacetransactioncallbackobjectdointransaction(connectionconn);调用伪代码:publicvoidmethoda()transactiontemplatetransactiontemplate=newtransactiontemplate();transactiontemplate.execute(newtransactioncallback()publicobjectdointransaction(connectionconn)/客户代码client.method1(1);/我们代码直接使用our.method2();/客户代码client.method3(l);returnnull;);/ronghao 荣浩原创,转载请注明出处:)posted on 2007-10-09 15:11 ronghao阅读(5090) 评论(5) 编辑 收藏所属分类: 工作日志高并发测试下的一些问题及解决测试在sqlserver2000上进行,对工作流操作的相关方法在单元测试里进行多线程并发。测试发现sqlserver出现死锁的情况相当多,一些典型的情况:1、对同一张表先insert再update是很快会引起死锁的,不管操作的是否是同一记录解决方法:对于同一记录,需要调整hibernate的映射策略,使得一次insert完成操作。对于不同的记录需要在代码中手动flush,使得update先于insert。2、对两张表进行多次update操作时,两张表交替update也会很快引起死锁解决方法:在代码中手动flush,保证对两张表的update不会出现交替的情况。3、部分大范围扫描的select和update混合也会导致死锁解决方法:优化sql,尽量减少sql语句,通过给po增加持久化字段的方式减少关联查询经过优化,大部分情况下数据库死锁的情况得以避免,另外奇怪的是通过事件探查器在死锁时并未发现锁升级的事件。但是在一些特殊情况下(例如多个并发汇聚的直接联合),死锁依旧发生。最后不得不对方法进行synchronized关键字同步,这个通过synchronized flush完成。业务方法不必同步,最后批量操作数据库时进行同步。换oracle进行测试,在未synchronized的情况下,未发生死锁情况。由此可见sqlserver与oracle锁实现机制存在很大的差别。对sqlserver鄙视之。另,同事说,sqlserver2005后性能和机制发生了很大的变化,未测试。补充一下我的一个最简单情况下的测试用例:po:publicclasstestpostringid;stringname;intnum;.映射文件 hibernate3:被测试方法(都配置有事务):publicvoidtestsave(intnum)testpopo=newtestpo();po.setname(ronghao);po.setnum(num);theadtestdao.save(po);po.setname(haorong);publicvoidtestsavebyjdbc(intnum)stringsql=insertintowfms_testpo(id,name,num)values(?,ronghao,?);objectparams=newobjectnum,num;jdbctemplate.update(sql,params);sql=updatewfms_testposetname=haorongwhereid=?;params=newobjectnum;jdbctemplate.update(sql,params);测试用例:publicvoidtestsave()throwsexceptiontheadtesttemplatetemplate=newtheadtesttemplate();template.execute(newtheadtestcallback()publicvoiddointhead(intsuquence)/theadtestmanager.testsave(suquence);theadtestmanager.testsavebyjdbc(suquence);,10);测试结果:不论是hibernate还是jdbc,并发情况下都很快就会引起sqlserver2000的死锁,换用两种数据库驱动jtds和jturbo死锁的情况没有变化。结论:sqlserver2000数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁支持很不完善,死锁情况非常容易发生。补充:我对数据库的一些实现机制也并不是很了解,所以这里也只能列出现象而不能解释死锁的根本原因。另外感谢alex的讨论。/ronghao 荣浩原创,转载请注明出处:)posted on 2008-06-19 13:34 ronghao阅读(2586) 评论(22) 编辑 收藏所属分类: 工作日志feedback:#re: 高并发测试下的一些问题及解决未登录2008-06-19 15:02 | alexxd ,你配置了事务了嘛?回复更多评论#re: 高并发测试下的一些问题及解决2008-06-19 17:55 | flybean1、死锁,还是锁阻塞,这是个问题。2、悲观并发、乐观并发生来以久,各有优缺点,搞清楚再来鄙视。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 09:14 | alex1、对同一张表先insert再update是很快会引起死锁的,不管操作的是否是同一记录2、对两张表进行多次update操作时,两张表交替update也会很快引起死锁3、部分大范围扫描的select和update混合也会导致死锁如果连这些问题都解决不了,你觉得是数据库问题,还是你的问题呢?别拿那么多名词出来吓人,这个招数我上小学的时候用来吓唬老师的,现在已经不用了。就捡最后一个说吧,乐观锁,如果你控制不了,还是就不要用了,您还就是老老实实的本分点,使用数据库本地提供的锁功能吧,牺牲点数据库的性能,也总比你数据更新失败要好,如果你非要使用hibernate的乐观锁,请控制好,内存中的数据和数据库中的数据一致性。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 09:28 | ronghaoalex我想你并没有弄明白问题的关键。乐观锁和悲观锁的作用是防止多个事务对同一数据操作产生冲突的两种策略。而我的问题是多个线程(即多个事务)并发操作不同的数据。不知道明不明白意思。ps:以前我也把这两者的概念经常混淆。回复更多评论#re: 高并发测试下的一些问题及解决2008-06-20 09:29 | beansoft说不定是滥用 hibernate 导致的后果回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 09:34 | ronghaobeansoft我倒真的认为是数据库的原因。一个简单的例子:很简单的po:string id;string name;两个字段,执行操作:testpo po=new testpo();po.setname(ronghao);dao.save(po);dao.flush();po.setname(haorong);配置事务,单元测试两个线程并发,sqlserver2000下马上死锁。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 09:39 | ronghaobeansoft当然,我并没有用jdbc直接测试回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:12 | alexronghao如果不是多线程,那么配置事务还有何用,如果所有操作都在同一个流水线上,那么配置事务不是浪费人民的感情嘛?回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:18 | alex使用hibernate乐观锁如果导致你保存失败,简单点说,就是你的两个线程或者多个线程所控制的内存中的数据和数据库中数据集已经不一致了。如果使用悲观锁,导致你保存失败,那么说明你事务配置的问题,或者至少说明你测试用例的事务配置有问题,要不你直接试试 jdbc 并且硬编码事务看看。否则 sqlserver2000 真的不要玩了,两个线程都是死锁。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:20 | alex同时操作同的记录集合,一方面要看你数据库使用什么lock策略,一方面还要看你代码的控制。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:21 | alex同时操作不同的记录集合,一方面要看你数据库使用什么lock策略,一方面还要看你代码的控制.回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:21 | ronghaoalex你的说法没有错。我想问题的原因在于死锁的原因你我的理解有差异。你理解的是对数据库表同一记录并发操作引起数据库死锁,这个显然是应用程序应该控制和避免的。而我的意思是并发操作某一动作,例如同时启动100个工作流流程,这个尽管操作的是同一张表但是显然是针对不同的记录,此时,乐观锁、悲观锁都是没有意义的。当然,我对数据库的一些实现机制也并不是很了解,所以这里也只能列出现象而不能解释死锁的根本原因。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:26 | ronghaoalex补充说明一下,在我的测试中,不会出现两个线程互相修改冲突的情况,甚至select也不会冲突,因为流程实例id一开始就将所有的数据区分开来。我是并发启动多个流程,然后并发提交这些流程。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:31 | alex上面我也说了,如果你直接使用hibernate的乐观锁,那么最细粒度的数据其实是由你自己来控制的,只要保持内存中的当前的数据在需要保存时和数据库中的保存相同的version 就可以了,然后不管有多少个线程insert 或者 update ,只要每个线程都满足这个要求,就能保存成功了,当然,如果你的数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁,那么肯定会像你所说的那样发生死锁。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:37 | alexronghao如果每个线程只处理某一个或一些独立流程id相关的数据,而没有交叉数据,那么还会出现这种现象。1.使用jdbc做这种测试,排除是否是hibernate的使用或者hibernate本身对2000支持的问题问题。(jdbc驱动也很关键。)2.查阅sql2000 对行锁的支持程度。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:45 | ronghaoalex哈哈,正如你说的,我想问题在这里:当然,如果你的数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁,那么肯定会像你所说的那样发生死锁。这也是我想表达的。我想有时间应该请个专业的dba来,我也查过了sqlserver的锁机制,但是具体到这里理解还是存在问题。另外你对乐观锁的理解也很到位。如果是多个事务同时提交一个流程,乐观锁就会起作用。回复更多评论#re: 高并发测试下的一些问题及解决未登录2008-06-20 10:46 | ronghaoalex好的,谢谢你的意见。我会用jdbc做这个测试。回复更多评论#r

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论