spring事务回滚处理:java
准备:配置好spring+mybatis环境。web
1、XML方式配置spring事务处理redis
第一步: spring.xml配置:spring
<tx:advice id="apptxAdvice" transaction-manager="txManager"> <tx:attributes> <!-- 须要回滚的方法都写在这 --> <tx:method name="trade*" read-only="false" rollback-for="Exception"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="appServiceOperation" expression="execution(* org.jun.service..*Service.*(..))" /> <aop:advisor pointcut-ref="appServiceOperation" advice-ref="apptxAdvice" /> </aop:config> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
第二步:编写测试代码:express
TestController.java package org.jun.controller; import org.jun.controller.base.AbstractController; import org.springframework.stereotype.Controller; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * * @author xiejunbo * **/ @Controller @RequestMapping("test") public class TestController extends AbstractController { @ResponseBody @RequestMapping("trade") public String trade(){ try { testService.trade(); } catch (Exception e) { logger.error("[error] trade error!" + e); } return "success"; } } ============================================================================== TestService.java package org.jun.service; /** * * @author xiejunbo * **/ public interface TestService { void trade() throws Exception; } ========================================================================== TestServiceImpl.java package org.jun.service; import javax.annotation.Resource; import org.jun.mapper.TestMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * * @author xiejunbo * **/ @Service public class TestServiceImpl implements TestService { @Resource private TestMapper testMapper; public void trade() throws Exception{ try { testMapper.updateBalance(); int i = 10/0; testMapper.record(); } catch (RuntimeException e) { throw new Exception(e); } } }
第三步:结果分析:mybatis
若是事务正常回滚,表中数据无变化。多线程
若是事务没有回滚,表中对应记录的余额被更新,但没有新添加的记录mvc
第四步:若是事务没法回滚:app
检查:确认有抛异常,确认扫描包的位置正确,确认没有重复扫描对应包。dom
一般只会在service层处理事务回滚操做.
1、注解方式配置spring事务处理
XML配置: spring.xml: <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="txManager"/> <context:component-scan base-package="org.web.service"> <context:exclude-filter type="annotation" expression="org.springframework.transaction.annotation.Transactional" /> </context:component-scan> springmvc.xml: <context:component-scan base-package="org.web.controller"/> TestController.java: package org.web.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.web.controller.base.AbstractController; import org.web.domain.User; @Controller @RequestMapping("test") public class TestController extends AbstractController{ @ResponseBody @RequestMapping("trade") public String trade() { try { User u = new User(); u.setId(1); u.setUsername("xiejunbo"); u.setPwd("123456"); testService.trade(u); } catch (Exception e) { logger.error("[error]" + e); } return "success"; } } TestServiceImpl.java: package org.web.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.web.domain.User; import org.web.mapper.TestMapper; import org.web.service.TestService; @Service public class TestServiceImpl implements TestService { @Autowired private TestMapper testMapper; @Transactional(rollbackFor=Exception.class) public void trade(User u) throws Exception { try { testMapper.substract(u); u.setId(2); testMapper.add(u); testMapper.record(u); int i = 10/0; } catch (Exception e) { throw new Exception(e); } } } 注意:扫描包时,对事务注解annotation只扫描一次,重复扫描会致使事务失效。
操做结果:
多线程异步处理: /*** 用户注册接口 @RequestMapping("/register") @ResponseBody public Resp register(final User user, String verifyCode, HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException { final String password = user.getPwd(); User result = null; Map<String, Integer> ageAndConstell = CommonHelper.getAgeAndConstell(user.getBirth()); try { result = resource.createUser(user); if (result != null) { // 异步注册 user.setId(result.getId()); threadPool.execute(new Runnable() { @Override public void run() { String imid = CryptUtil.md5(user.getPhone()); String username = hx.register(imid, password, user.getNick()); if (StringUtils.isNotBlank(username)) { // 注册成功 user.setImid(imid); redis.set(Consts.Cache.USER_PREFIX + user.getId(), user); } else { //注册失败 log.error("register error imid:{}", imid); } } }); } } catch (Exception e) { log.error("[/user/register] error ", e); return error(e); } return result != null ? success(user, "注册成功") : fail(); // 将user返回 } @Autowired protected ThreadPool threadPool;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class ThreadPool extends ThreadPoolExecutor { private String poolName; /** * 建立线程数固定大小的线程池 * * @param poolSize * @param poolName 线程池的名称必须设置 */ public ThreadPool(int poolSize, String poolName) { super(poolSize, poolSize, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NamingThreadFactory( poolName)); this.poolName = poolName; } public String getPoolName() { return poolName; } public void setPoolName(String poolName) { this.poolName = poolName; } private static class NamingThreadFactory implements ThreadFactory { private String threadName; private AtomicInteger counter = new AtomicInteger(1); public NamingThreadFactory(String threadName) { this.threadName = threadName; } @Override public Thread newThread(Runnable r) { int index = counter.getAndIncrement(); return new Thread(r, threadName + "-" + index); } } public String toString() { String str = super.toString(); int idx = str.indexOf("["); if (idx == -1) { return "[name = " + poolName + "]"; } String s = str.substring(idx + 1); return "[name = " + poolName + ", " + s; } }