27. 代码实例-spring声明式事务
(1)spring配置文件:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 3 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 7 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 9 http://www.springframework.org/schema/util classpath:/org/springframework/beans/factory/xml/spring-util-3.0.xsd 10 " 11 default-autowire="byName"> 12 <bean id="paymentSqlSqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 13 <property name="configLocation" value="classpath:sqlmap/sqlserver/sqlserver-consume.xml" /> 14 <property name="dataSource" ref="paymentSqlDataSource" /> 15 </bean> 16 17 <!-- ============================ payment ======================================= --> 18 <bean id="paymentSqlDataSource" class="org.apache.commons.dbcp.BasicDataSource"> 19 <property name="driverClassName" value="${sqlserver.jdbc.driver}" /> 20 <property name="url" value="${payment.sqlserver.jdbc.url}" /> 21 <property name="username" value="${payment.sqlserver.jdbc.username}" /> 22 <property name="password" value="${payment.sqlserver.jdbc.password}" /> 23 <property name="maxActive" value="30" /> 24 <property name="initialSize" value="2" /> 25 <property name="maxWait" value="60000" /> 26 <property name="maxIdle" value="30" /> 27 <property name="minIdle" value="1" /> 28 <property name="testOnBorrow" value="false"></property> 29 <property name="testWhileIdle" value="true"></property> 30 <property name="validationQuery" value="select 1 "></property> 31 <property name="timeBetweenEvictionRunsMillis"> 32 <value>300000</value> 33 </property> 34 <property name="numTestsPerEvictionRun"> 35 <value>10</value> 36 </property> 37 <property name="minEvictableIdleTimeMillis" value="300000"></property> 38 </bean> 39 <bean id="consumeTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate" > 40 <property name="sqlMapClient" ref="paymentSqlSqlMapClient" /> 41 </bean> 42 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 43 <property name="dataSource" ref="paymentSqlDataSource" /> 44 </bean> 45 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> 46 </beans>
(2)事务相关代码
1 package com.jd.consume.service.consume.impl; 2 3 import java.util.List; 4 5 import javax.annotation.Resource; 6 7 import org.apache.commons.collections.CollectionUtils; 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.stereotype.Service; 11 import org.springframework.transaction.annotation.Propagation; 12 import org.springframework.transaction.annotation.Transactional; 13 14 import com.jd.consume.dao.ConsumeDao; 15 import com.jd.consume.dao.OrderTaskDao; 16 import com.jd.consume.domain.ConsumeDetail; 17 import com.jd.consume.domain.OrderTask; 18 import com.jd.consume.service.consume.ConsumeChange; 19 20 /** 21 * 消费额度变更服务 22 * @author guanpanpan 23 * 24 */ 25 @Service(value = "consumeChange") 26 @Transactional(value = "transactionManager", readOnly = true) 27 public class ConsumeChangeImpl implements ConsumeChange { 28 protected final static Logger log = LoggerFactory.getLogger(ConsumeChangeImpl.class); 29 @Resource 30 private OrderTaskDao orderTaskDao; 31 @Resource 32 private ConsumeDao consumeDao; 33 public static boolean throwRuntimeExceptionInTransation = false; 34 35 @Transactional(value = "transactionManager", readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRED) 36 public void addConsumeWithSingleTask(OrderTask orderTask) { 37 ConsumeDetail consumeDetail = orderTask.genAddConsumeDetail(); 38 //防止重复执行,如果存在uuid置状态为11 39 List<ConsumeDetail> consumeDetails = consumeDao.findConsumeDetailsByUuid(consumeDetail.getUuId()); 40 if (CollectionUtils.isNotEmpty(consumeDetails)) { 41 orderTaskDao.updateStatus(orderTask.getId(), OrderTask.SYN_REPEAT, OrderTask.SYN_LOCK); 42 return; 43 } 44 consumeDao.addConsumeDetail(consumeDetail); 45 consumeDao.insertOrUpdateJdConsume(consumeDetail); 46 //设置状态为已处理,如果失败会在回滚中置好状态 47 orderTaskDao.updateStatus(orderTask.getId(), OrderTask.SYN_COMPLETE, OrderTask.SYN_LOCK); 48 if (throwRuntimeExceptionInTransation) { 49 throw new RuntimeException("rollBack in Test"); 50 } 51 } 52 53 @Override 54 @Transactional(value = "transactionManager", readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRED) 55 public void reduceConsumeWithSingleTask(OrderTask orderTask) { 56 ConsumeDetail consumeDetail = orderTask.genPastConsumeDetail(); 57 //防止重复执行,如果存在uuid置状态为12 58 List<ConsumeDetail> consumeDetails = consumeDao.findConsumeDetailsByUuid(consumeDetail.getUuId()); 59 if (CollectionUtils.isNotEmpty(consumeDetails)) { 60 orderTaskDao.updatePastStatus(orderTask.getId(), OrderTask.PAST_REPEAT, OrderTask.PAST_LOCK); 61 return; 62 } 63 //设置状态为已处理,如果失败会在回滚中置好状态 64 orderTaskDao.updatePastStatus(orderTask.getId(), OrderTask.PAST_DUE_YET, OrderTask.PAST_LOCK); 65 consumeDao.addConsumeDetail(consumeDetail); 66 consumeDao.insertOrUpdateJdConsume(consumeDetail); 67 if (throwRuntimeExceptionInTransation) { 68 throw new RuntimeException("rollBack in Test"); 69 } 70 71 } 72 73 }
(3)编码式事务
有时不能使用声明时事务,比如分库分表。声明式的库是固定的。
1 package com.jd.consume.service.consume.impl; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import javax.annotation.Resource; 7 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.jdbc.datasource.DataSourceTransactionManager; 11 import org.springframework.stereotype.Service; 12 import org.springframework.transaction.TransactionDefinition; 13 import org.springframework.transaction.TransactionStatus; 14 import org.springframework.transaction.support.DefaultTransactionDefinition; 15 import org.springframework.util.CollectionUtils; 16 17 import com.jd.consume.dao.moredb.DbManager; 18 import com.jd.consume.dao.mysql.ConsumeDaoMysql; 19 import com.jd.consume.dao.mysql.ConsumeDetailDaoMysql; 20 import com.jd.consume.dao.mysql.OrderTaskDaoMysql; 21 import com.jd.consume.domain.ConsumeDetail; 22 import com.jd.consume.domain.JdConsume; 23 import com.jd.consume.domain.OrderTask; 24 import com.jd.consume.service.consume.CalJdconsume; 25 import com.jd.consume.service.consume.ConsumeChange; 26 import com.jd.consume.util.LogUtil; 27 import com.jd.consume.util.PropertyUtil; 28 29 /** 30 * 消费额度变更服务 31 * @author guanpanpan 32 * 33 */ 34 @Service(value = "consumeChange") 35 //@Transactional(value = "transactionManager", readOnly = true) 36 public class ConsumeChangeImpl implements ConsumeChange { 37 protected final static Logger log = LoggerFactory.getLogger(ConsumeChangeImpl.class); 38 @Resource 39 private OrderTaskDaoMysql orderTaskDaoMysql; 40 @Resource 41 private ConsumeDaoMysql consumeDaoMysql; 42 @Resource 43 private ConsumeDetailDaoMysql consumeDetailDaoMysql; 44 @Resource 45 private DbManager dbManager; 46 @Resource(name = "calJdconsumeFind") 47 private CalJdconsume calJdconsumeFind; 48 @Resource(name = "calJdconsumeCal") 49 private CalJdconsume calJdconsumeCal; 50 private boolean calJdconsumeByFind = PropertyUtil.getBooleanParameter("calJdconsumeByFind");//默认用数据库计算,减少传输 51 public static boolean throwRuntimeExceptionInTransation = false; 52 public static ThreadLocal<Set<String>> reDianPin = new ThreadLocal<Set<String>>();//热点数据 53 public static ThreadLocal<Long> reDianLastTime = new ThreadLocal<Long>();//热点数据 54 public static boolean isReDianFlag = true; 55 public static int reDianExePerTime = 5;//分钟,每几分钟执行一下热点 56 public static int reDianMomeryMax = 100;//最多缓存热点数据 57 public static boolean isUpGrade = false;//是否需要升级,新版本升级程序上线后就不需要再升级了 58 59 //@Transactional(value = "transactionManager", readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRED) 60 public void addConsumeWithSingleTask(int dbNo, OrderTask orderTask) { 61 //因为分库所以不能用注解 62 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 63 def.setReadOnly(false); 64 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 65 DataSourceTransactionManager transactionManager = dbManager.getTransactionManager(dbNo); 66 TransactionStatus status = transactionManager.getTransaction(def); 67 try { 68 ConsumeDetail consumeDetail = orderTask.genAddConsumeDetail(); 69 //防止重复执行,如果存在uuid置状态为11 70 ConsumeDetail consumeDetailDb = consumeDetailDaoMysql 71 .findConsumeDetailsByUuid(orderTask.getPin(), consumeDetail.getUuId()); 72 if (null != consumeDetailDb) { 73 orderTaskDaoMysql.updateStatus(dbNo, orderTask.getId(), OrderTask.SYN_REPEAT, OrderTask.SYN_LOCK); 74 transactionManager.commit(status); 75 return; 76 } 77 consumeDetailDaoMysql.addConsumeDetail(consumeDetail); 78 if (isReDianFlag) { 79 JdConsume jdConsume = consumeDaoMysql.getJdConsumeByPin(orderTask.getPin()); 80 if (null == jdConsume || !jdConsume.isReDian()) { 81 calAndSaveJdConsumeAmounts(orderTask.getPin());//计算消费额度 82 //设置状态为已处理,如果失败会在回滚中置好状态 83 updateAddStatus(dbNo, orderTask); 84 } else { 85 //加入热点集合去重 86 Set<String> reDianSet = reDianPin.get(); 87 if (reDianSet == null) { 88 reDianSet = new HashSet<String>(); 89 } 90 reDianSet.add(orderTask.getPin()); 91 reDianPin.set(reDianSet); 92 //设置时间 93 if (reDianLastTime.get() == null) { 94 reDianLastTime.set(System.currentTimeMillis()); 95 } 96 //设置状态为已处理,如果失败会在回滚中置好状态 97 orderTaskDaoMysql.updateStatus(dbNo, orderTask.getId(), OrderTask.SYN_REDIAN_ADD, OrderTask.SYN_LOCK); 98 99 } 100 } else { 101 calAndSaveJdConsumeAmounts(orderTask.getPin());//计算消费额度 102 updateAddStatus(dbNo, orderTask); 103 } 104 105 if (throwRuntimeExceptionInTransation) { 106 throw new RuntimeException("rollBack in Test"); 107 } 108 } catch (Exception e) { 109 transactionManager.rollback(status); 110 throw new RuntimeException(e); 111 } 112 transactionManager.commit(status); 113 } 114 115 private void updateAddStatus(int dbNo, OrderTask orderTask) { 116 if (isUpGrade) { 117 orderTaskDaoMysql.updateStatus(dbNo, orderTask.getId(), OrderTask.SYN_COMPLETE, OrderTask.SYN_LOCK); 118 } else { 119 orderTaskDaoMysql.updateStatusNoGrade(dbNo, orderTask.getId(), OrderTask.SYN_COMPLETE, OrderTask.SYN_LOCK); 120 } 121 122 } 123 124 private void updateAddStatusRedian(int dbNo, String pin) { 125 if (isUpGrade) { 126 orderTaskDaoMysql.updateStatus(dbNo, pin, OrderTask.SYN_COMPLETE, OrderTask.SYN_REDIAN_ADD); 127 } else { 128 orderTaskDaoMysql.updateStatusNoGrade(dbNo, pin, OrderTask.SYN_COMPLETE, OrderTask.SYN_REDIAN_ADD); 129 } 130 131 } 132 133 @Override 134 public void dealRedianDatas(int dbNo, int threadId) { 135 Set<String> reDianSet = reDianPin.get(); 136 if (CollectionUtils.isEmpty(reDianSet)) { 137 return; 138 } 139 140 //5分钟执行一次 141 boolean isTime = (System.currentTimeMillis() - reDianLastTime.get()) > 1000 * 60 * reDianExePerTime; 142 if (isTime || reDianSet.size() > reDianMomeryMax) { 143 for (String pin : reDianSet) { 144 calAndSaveJdConsumeAmounts(pin);//计算消费额度 145 updateAddStatusRedian(dbNo, pin); 146 } 147 //重置时间 148 reDianLastTime.set(System.currentTimeMillis()); 149 //重置集合 150 reDianPin.set(new HashSet<String>()); 151 LogUtil.error(LogUtil.ADDCONSUME, "reDian:" + isTime + reDianSet); 152 LogUtil.error(LogUtil.DT, "reDian:" + isTime + "-" + dbNo + "-" + threadId + reDianSet); 153 } 154 } 155 156 @Override 157 public void calAndSaveJdConsumeAmounts(String pin) { 158 JdConsume jdConsume = calJdconsume(pin); 159 consumeDaoMysql.insertOrUpdateJdConsume(jdConsume); 160 } 161 162 @Override 163 public JdConsume calJdconsume(String pin) { 164 if (calJdconsumeByFind) { 165 return calJdconsumeFind.calJdconsume(pin); 166 } else { 167 return calJdconsumeCal.calJdconsume(pin); 168 } 169 170 } 171 172 }
注:1,2代码来源于consume-grade-sql,3来源于consume-grade-final(mysql版本)
减少重复学习,在原来掌握的基础上进行提高。质疑驱动,不断对已经掌握的知识进行质疑。
如果能把已经掌握的东西总结成模式,才是真正掌握了。
浙公网安备 33010602011771号