在公司开发一个后台管理系统时有这样的需求:提交一个表单时,要把表单域内容和上传的文件内容(可以是多个上传文件)一并提交到后台去,并且数据库持久化失败后数据要回滚且文件不应该上传上去,如果文件上传失败同样数据库也要回滚。
我的做法是:
1. Spring MVC的controller只是将参数包装成DTO,提交给service层一并处理文件上传和数据库保存操作。controller中的方法,如:
@RequestMapping("save.do") public @ResponseBody String storySave(MultipartHttpServletRequest request, StoryDTO storyDTO, String[] fileName) throws Exception { storyDTO.setFiles(request.getFileMap()); storyDTO.setFileUploadPath(webConfig.getFileUploadPath()); storyDTO.setFileName(fileName); try { storyService.processingStory(storyDTO); } catch (Exception e) { e.printStackTrace(); return "{\"status\":\"err\"}"; } return "{\"status\":\"ok\"}"; }
在service里的方法processingStory是被Spring事务管控的,那么这个方法头部就抛出一个Exception异常,且在这个方法里的操作一定是数据持久化操作在先,而文件操作在后。
文件操作的潜在的异常也是由processingStory这个方法统一抛出的。service中的处理方法如下:
@Override public void processingStory(StoryDTO storyDTO) throws Exception { Map<String, MultipartFile> files = storyDTO.getFiles(); Boolean file1=null,file2=null,file3=null,file4=null; String[] fileName = storyDTO.getFileName(); String fileUploadPath = storyDTO.getFileUploadPath(); String[] pathNames = new String[4]; InputStream[] ins = new InputStream[4]; for (Entry<String, MultipartFile> entry : files.entrySet()) { if("toUpload0".equals(entry.getKey())) { file1=true; String pathName = fileUploadPath+entry.getValue().getOriginalFilename(); pathNames[0] = pathName; ins[0] = entry.getValue().getInputStream(); storyDTO.setPosterUrl1(pathName); } if("toUpload1".equals(entry.getKey())) { file2=true; String pathName = fileUploadPath+entry.getValue().getOriginalFilename(); pathNames[1] = pathName; ins[1] = entry.getValue().getInputStream(); storyDTO.setPosterUrl2(pathName); } if("toUpload2".equals(entry.getKey())) { file3=true; String pathName = fileUploadPath+entry.getValue().getOriginalFilename(); pathNames[2] = pathName; ins[2] = entry.getValue().getInputStream(); storyDTO.setPosterUrl3(pathName); } if("toUpload3".equals(entry.getKey())) { file4=true; String pathName = fileUploadPath+entry.getValue().getOriginalFilename(); pathNames[3] = pathName; ins[3] = entry.getValue().getInputStream(); storyDTO.setPosterUrl4(pathName); } } if(null == storyDTO.getStoryId()) { this.saveStory(storyDTO); } else { String[] deletePaths = new String[4]; StoryDTO oldStoryDTO = this.getStoryById(storyDTO.getStoryId()); if(file1==null && StringUtils.isBlank(fileName[0])){ deletePaths[0]=oldStoryDTO.getPosterUrl1(); storyDTO.setPosterEmptyUrl1("Y"); } if(file2==null && StringUtils.isBlank(fileName[1])){ deletePaths[1]=oldStoryDTO.getPosterUrl2(); storyDTO.setPosterEmptyUrl2("Y"); } if(file3==null && StringUtils.isBlank(fileName[2])){ deletePaths[2]=oldStoryDTO.getPosterUrl3(); storyDTO.setPosterEmptyUrl3("Y"); } if(file4==null && StringUtils.isBlank(fileName[3])){ deletePaths[3]=oldStoryDTO.getPosterUrl4(); storyDTO.setPosterEmptyUrl4("Y"); } StoryUpdateDTO updateDTO = new StoryUpdateDTO(); BeanUtils.copyProperties(updateDTO, storyDTO); updateDTO.setUpdateStoryId(storyDTO.getStoryId()); this.updateStory(updateDTO); for (String path : deletePaths) { if(StringUtils.isNotBlank(path)) { FileOprUtils.deleteFile(path); } } } for(int i = 0; i < pathNames.length; i++) { FileOprUtils.copyFileToServerPath(pathNames[i], ins[i]); } }
最后是Spring的声明式事务配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="rollbackOnCommitFailure" value="true" /> <property name="globalRollbackOnParticipationFailure" value="true" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" rollback-for="Exception" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.focoon.ds..service.*.*(..))" /> </aop:config>
注意需要使用rollback-for属性明确指出抛出哪种异常需要回滚
相关推荐
它还提供了一组简明的JavaScript事件,借助它们开发者可以方便的在文件上传过程中更新页面内容来营造各种动态效果。 在使用SWFUpload之前,请确认你具备一定的JavaScript和DOM知识。在实际开发中,大部分的错误都...
10 支持在提交编辑文档的同时,提交表单的其它数据,包括其它的文件上传 采用我们独创的“智能提交”技术,让您在提交编辑文档的同时,提交表单的其它数据,以及表单中其它的文件上传。这可以让您更加快速的集成现有...
★ 采用我们独创的“智能提交”技术,基于国际标准提交数据而非自定义协议,让您在提交编辑文档的同时,提交表单的其它数据,以及表单中其它的文件上传。这可以让您更加快速的集成现有的应用程序,或者创建新的应用...
首页明星榜设置:首先进入快捷方式——管理首页,在这个表单中选择上传头像复选框,如果需要设置是区分性别的,可以在这个表单中选定。然后点击[给我搜]按钮进入用户列表。然后在搜索结果列表中操作选定要设置的...
分布式服务下的交易一致性原理及解决 分布式服务框架(dubbo+zookpeer) WEB高级前后台分离思维-懒加载无限级树形菜单 动态页面的静态化处理 大并发展示优化,动态页面的静态化 深入理解JDK动态代理本质 企业级高并发...
■建站:版块表单功能,提交的表单后台存储 ■建站:模板增加预览功能(PC,iPad,iPhone) ■建站:使用新的编辑器替代ckeditor ■建站:版块数据管理功能(content|image等) ■修复:UEditor编辑器,图片上传Bug ■修复:...
第2章 并发操作的一致性问题 (2) Using sqlite with .NET Visual Studio 2005 中的新 DataSet 特性 MySQL 和 .Net2.0配合使用 与DotNet数据对象结合的自定义数据对象设计 (二) 数据集合与DataTable 与DotNet数据对象...
-v0.2beta2版本中关于PersistChildren(true)的描述有误,这个是设计时属性,和运行时是否保持状态没有关系。 -修正CheckBox控件的CheckedChanged事件会被触发两次的BUG(Data PostBack->AutoPostBack, Event ...
17. 多(简英)语版中,每种语言可制作出完全不一样的内容,以方便不同语种针对不同浏览习惯的用户,当然也可制作完全一样的内容,保持内容的一致性。如果你觉得英语页面不不需要轻松实现开关功能,也可将其改为其他...
支持后台访问目录更名、Cookie加密、验证码、认证码、IP锁定、IP白名单、防SQL注入、防跨站脚本、防脚本文件上传等多重安全机制,并且后台支持按频道和模块严格控制访问权限,为网站的安全运营提供最强有力的保障。...
2、数据的一致性与完整性。3、数据的共享与独立性。 2.2. 系统的可行性分析 2.2.1. 技术可行性 技术上的可行性分析要考虑将来要采用的硬件和软件技术能否满足用户(这里是服务器,网速)提出的要求(如计算机的...
-v0.2beta2版本中关于PersistChildren(true)的描述有误,这个是设计时属性,和运行时是否保持状态没有关系。 -修正CheckBox控件的CheckedChanged事件会被触发两次的BUG(Data PostBack->AutoPostBack, Event ...