权限模块-修改操作存在问题

本文通过一个实际案例探讨了Spring框架中事务管理的重要性。当涉及到数据库的增删改操作时,特别是包含多个操作的情况,确保所有操作要么全部成功要么全部失败至关重要。文章详细介绍了如何通过Spring AOP实现事务管理,包括配置事务、异常捕获与回滚的两种方法。

大多数后台程序都有后台程序,显然也就有权限管理模块。今天在做项目时发现了一个重大问题(差点害死我)

先看一下权限模块-修改的效果图:

105954_dtaE_2475326.png

110159_1aqC_2475326.png

110441_IUxf_2475326.png

152139_c2PH_2475326.png

到这一步有什么问题吗?看着savaPermit()方法页很正常,而且还try-catch了,使用boolean result作为标记,标记是否修改成功。

但仔细想一想,这个方法存在问题:这个修改操作(对数据库一个delete,多个insert),应该是个原子操作,要么都成功,要么就需在在数据库中回滚

我当时在debug这断代码,delete操作之后,可能是修改了源码,eclipse重新编译-部署了一次,删除操作之后,保存操作出现了异常。发现了这个问题。

(下面是debug重新部署的log:)

十一月 01, 2016 1:38:30 下午 org.apache.catalina.core.StandardContext reload
信息: Reloading Context with name [/supervise-copy] has started
十一月 01, 2016 1:38:30 下午 org.apache.catalina.core.StandardWrapper unload
信息: Waiting for 1 instance(s) to be deallocated for Servlet [springMVC]
十一月 01, 2016 1:38:31 下午 org.apache.catalina.core.StandardWrapper unload
信息: Waiting for 1 instance(s) to be deallocated for Servlet [springMVC]
十一月 01, 2016 1:38:32 下午 org.apache.catalina.core.StandardWrapper unload
信息: Waiting for 1 instance(s) to be deallocated for Servlet [springMVC]
十一月 01, 2016 1:38:32 下午 org.apache.catalina.core.ApplicationContext log
信息: Destroying Spring FrameworkServlet 'springMVC'
十一月 01, 2016 1:38:32 下午 org.apache.catalina.core.ApplicationContext log
信息: Closing Spring root WebApplicationContext
十一月 01, 2016 1:38:32 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesJdbc
严重: The web application [/supervise-copy] registered the JDBC driver [oracle.jdbc.driver.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
十一月 01, 2016 1:38:32 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
严重: The web application [/supervise-copy] is still processing a request that has yet to finish. This is very likely to create a memory leak. You can control the time allowed for requests to finish by using the unloadDelay attribute of the standard Context implementation.
十一月 01, 2016 1:38:32 下午 org.apache.catalina.loader.WebappClassLoaderBase checkThreadLocalMapForLeaks
严重: The web application [/supervise-copy] created a ThreadLocal with key of type [org.springframework.core.NamedThreadLocal] (value [Request attributes]) and a value of type [org.springframework.web.context.request.ServletRequestAttributes] (value [org.apache.catalina.connector.RequestFacade@7b3f82eb]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
十一月 01, 2016 1:38:32 下午 org.apache.catalina.loader.WebappClassLoaderBase checkThreadLocalMapForLeaks
严重: The web application [/supervise-copy] created a ThreadLocal with key of type [org.springframework.core.NamedThreadLocal] (value [Locale context]) and a value of type [org.springframework.web.servlet.DispatcherServlet$1] (value [org.springframework.web.servlet.DispatcherServlet$1@c1fd6b2]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
十一月 01, 2016 1:38:36 下午 org.apache.catalina.startup.TldConfig execute
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
十一月 01, 2016 1:38:36 下午 org.apache.catalina.core.ApplicationContext log
信息: No Spring WebApplicationInitializer types detected on classpath
十一月 01, 2016 1:38:36 下午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring root WebApplicationContext
十一月 01, 2016 1:38:38 下午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring FrameworkServlet 'springMVC'
十一月 01, 2016 1:38:40 下午 org.apache.catalina.core.StandardContext reload
信息: Reloading Context with name [/supervise-copy] is completed

(下面是报的异常信息:)

133214_C83E_2475326.png

然后:

111612_2jtM_2475326.png我累个擦,这个用户是tmd超级管理员啊。悲催了。。。。。

还好我在另外的数据库中有备份。用备份恢复之后,终于终于好了。

 

但是这个如何解决呢?

配置事务

    Spring AOP异常捕获原理
        被拦截的方法需显式抛出异常,并不能经过任何处理,这样aop代理才能捕获到方法存在异常,才能进行回滚。默认情况下aop只捕获RuntimeException的异常,但可以通过<tx:method name="upd*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>配置来捕获特定的异常并回滚

        换句话说在service的方法中不使用try-catch或者在catch中最后加上throw new runtimeexcetpion();,这样此方法的异常时才能被aop捕获进而回滚。

 

    解决方案:

        方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException();语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端、view层、action层)要继续捕获这个异常并做相应处理。

        方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)

    注意:

        无论方案1还是方案2,都需要配置AOP切面:(一开始我就是没有配置AOP切面,就一直不能回滚。)

	<!-- 事物配置 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED"
				rollback-for="java.lang.Exception" />
			<tx:method name="*" propagation="SUPPORTS" />
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut id="canyin"
			expression="execution(* com.shark.service.impl.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="canyin" />
	</aop:config>

 

如果使用方案2:Controller无需修改。

Service:(要在Service类上加上@Transactional(rollbackFor = Exception.class)注解)

	@Override
	public boolean savePermit(Integer roleId, String moduleId) {
		boolean result = false;
		try {
			iSysPermitDAO.deleteByRoleId(roleId);
			Map<String, Integer> map = new HashMap<String, Integer>();
			String[] strArray = moduleId.split("\\|");
			for (String str : strArray) {
				int mid = Integer.parseInt(str);
				map.put("roleId", roleId);
				map.put("moduleId", mid);
				map.put("functionIds", 1);
				iSysPermitDAO.insert(map);
			}
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		}
		return result;
	}

如果使用方案1:

Service:(要在Service类上加上@Transactional(rollbackFor = Exception.class)注解)

	@Override
	public boolean savePermit(Integer roleId, String moduleId) {
		boolean result = false;
		try {
			iSysPermitDAO.deleteByRoleId(roleId);
			Map<String, Integer> map = new HashMap<String, Integer>();
			String[] strArray = moduleId.split("\\|");
			for (String str : strArray) {
				int mid = Integer.parseInt(str);
				map.put("roleId", roleId);
				map.put("moduleId", mid);
				map.put("functionIds", 1);
				iSysPermitDAO.insert(map);
			}
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
//			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			throw new RuntimeException();
		}
		return result;
	}

Controller:

	@ResponseBody
	@RequestMapping("savePermit.do")
	public boolean savePermit(Integer roleId, String moduleId) {
		boolean f = false;
		try {
			f = sysPermitService.savePermit(roleId, moduleId);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return f;
	}

 

参考:

    spring 声明式事务配置,主动抛出异常不回滚。http://bnmnba.iteye.com/blog/1402071

转载于:https://my.oschina.net/anxiaole/blog/779212

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值