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; } }