tx
一、编程式事务辅助类
@Component public class TxHelper { @Autowired private PlatformTransactionManager tx; public TransactionStatus start() { DefaultTransactionDefinition txDef = new DefaultTransactionDefinition(); return tx.getTransaction(txDef); } public void commit(TransactionStatus txStatus) { tx.commit(txStatus); } public void rollback(TransactionStatus txStatus) { tx.rollback(txStatus); } }
注解异常catch中手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
<=>
@Autowired private PlatformTransactionManager tx; DefaultTransactionDefinition txDef = new DefaultTransactionDefinition(); TransactionStatus txStatus = tx.getTransaction(txDef); tx.rollback(txStatus);
二、springboot jpa @Transactional
1.controller
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(path = "test") public class FacadeController { @Autowired private FruitService fruitService; @Autowired private FacadeService facadeService; @Autowired private PlatformTransactionManager tx; @RequestMapping(value = "testinit") public String test(String a) { return (tx==null) + ""; } @RequestMapping(value = "add") public String addFruit(Fruit fruit) { Fruit result = fruitService.addFruit(fruit); return "SUCCESS"; } @RequestMapping(value = "update") public String updateFruitPrice(Fruit fruit) { fruitService.updateFruitPrice(fruit); return "SUCCESS"; } @RequestMapping(value = "updateThread") public String updateFruitPriceThread(Fruit fruit) { facadeService.updateFruitPrice(fruit); return "SUCCESS"; } }
2.实体类entity
import lombok.Data; import javax.persistence.*; import java.math.BigDecimal; @Data @Entity @Table(name = "zz_test") public class Fruit { @Id @Column(name = "uid") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name", unique = true, columnDefinition = "VARCHAR(32) DEFAULT NULL COMMENT 'fruit name'") private String fruitName; @Column(columnDefinition = "VARCHAR(32) DEFAULT NULL COMMENT 'fruit color'") private String color; @Column(nullable = false, columnDefinition = "decimal(20,4) DEFAULT 0 COMMENT 'fruit price'") private BigDecimal price; @Column(columnDefinition = "tinyint(2) default 0 COMMENT '价格是否超过10元'") private Boolean moreThan10; }
3.dao层
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.math.BigDecimal; @Repository public interface FruitDao extends JpaRepository<Fruit, Long>, JpaSpecificationExecutor<Fruit> { @Modifying @Query(value = "update Fruit f set f.price = ?2 where f.id = ?1") void updatePrice(Long id, BigDecimal price); @Modifying @Query(value = "update Fruit f set f.moreThan10 = 1 where f.id = ?1") void updateFlag(Long id); }
4. service接口层
public interface FacadeService { void updateFruitPrice(Fruit fruit); }
public interface FruitService { void updateFruitPrice(Long id, BigDecimal price); /** * 自身开启事务,调用默认级别事务方法保存 * @param fruit * @return */ Fruit addFruit(Fruit fruit); /** * 使用自身事务 * @param fruit * @param selfTx * @return */ Fruit addFruit(Fruit fruit, Boolean selfTx); void updateFruitPrice(Fruit fruit); void updateFlag(Fruit fruit); void updateFlagOtherClz(Fruit fruit); }
5.实现层
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j @Service public class FacadeServiceImpl implements FacadeService { @Autowired private FruitDao fruitDao; @Autowired private FruitService fruitService; public static final ExecutorService pool = Executors.newFixedThreadPool(2); @Override @Transactional(rollbackFor = Throwable.class) public void updateFruitPrice(Fruit fruit) { fruitService.updateFruitPrice(fruit); //模拟:数据必须正常记录,不相关的异常不能影响数据记录。 try { /* 本类中调用, 事务不开启,多线程异步处理,不受本方法事务管理*/ updateFlagThisClz(fruit); /* 注解经过aop切面, 事务正常开启,多线程异步处理,标识受到事务管理,回滚不更新,只更新价格 */ //fruitService.updateFlagOtherClz(fruit); } catch (Exception e) { e.printStackTrace(); } } //多线程模拟。数据更新后,需要去更新对应的业务数据表,然后再更新本表中的某字段。(如此中的价格标识字段) @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW) public void updateFlagThisClz(final Fruit fruit) { pool.execute(() -> { final BigDecimal price = fruit.getPrice(); if(price != null && price.compareTo(BigDecimal.TEN) > 0) { log.info("价格超过10元,更新标识"); /* 直接调用dao层,没有事务 * Exception in thread "pool-1-thread-3" org.springframework.dao.InvalidDataAccessApiUsageException: * Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: * Executing an update/delete query * */ fruitDao.updateFlag(fruit.getId()); //----------------------------------------------------------------- /* 可以成功更新标识,多线程异步处理时,不受本类调用事务的管理 使用的是下面方法调用的事务,调用方法之后的异常不对事务生效 */ // fruitService.updateFlag(fruit); // int temp = 1/0; //----------------------------------------------------------------- } }); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionTemplate; import java.math.BigDecimal; @Slf4j @Service public class FruitServiceImpl implements FruitService { @Autowired private FruitDao fruitDao; @Autowired private PlatformTransactionManager tx; @Autowired private TransactionTemplate template; @Override @Transactional public Fruit addFruit(Fruit fruit, Boolean selfTx) { Fruit data = fruitDao.save(fruit); return data; } /** * 同一个类中调用其他的事务方法,不论是否要求开启新的事务,都默认加入当前事务。如果当前方法没有事务,则没有事务启用。 * reason: 同一个类中调用没有经过代理,事务注解没有切入点切入,无法开启事务 * @param fruit * @return */ @Override @Transactional(rollbackFor = Throwable.class) public Fruit addFruit(Fruit fruit) { Fruit data = saveData(fruit); int temp = 1/0; //1.调用本类方法事务没有开启。2.经过切面开启事务后,如果没有新建事务需求,加入本方法事务。皆可正常回滚 return data; } /* addFruit 没有事务时(注释://@Transactional(rollbackFor = Throwable.class)) 同一个类中调用没有经过代理,事务注解没有切入点切入,无法开启事务 @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW) public Fruit saveData(Fruit fruit) { Fruit po = fruitDao.save(fruit); int a = 1/0; //抛异常之前数据已保存。异常后不会回滚 return po; } * */ @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW) public Fruit saveData(Fruit fruit) { Fruit po = fruitDao.save(fruit); return po; } /** * 编程式事务。 * 同一个类中调用,手动开启事务。 */ public Fruit saveData(Fruit fruit, Boolean manualTx) { DefaultTransactionDefinition txDef = new DefaultTransactionDefinition(); txDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);//(Propagation.REQUIRES_NEW.value()); TransactionStatus ts = tx.getTransaction(txDef); Fruit po = null; try { po = fruitDao.save(fruit); int a = 1/0; tx.commit(ts); } catch (Exception e) { e.printStackTrace(); tx.rollback(ts); } //=========================TransactionTemplate 方式========================= /*Fruit po2 = template.execute(new TransactionCallback<Fruit>() { @Override public Fruit doInTransaction(TransactionStatus ts) { try { return fruitDao.save(fruit); } catch (Exception e) { e.printStackTrace(); ts.setRollbackOnly(); //设置回滚 throw e; } } });*/ return po; } @Override @Transactional public void updateFruitPrice(Long id, BigDecimal price) { fruitDao.updatePrice(id, price); } @Override public void updateFruitPrice(Fruit fruit) { updateFruitPrice(fruit.getId(), fruit.getPrice()); } @Override @Transactional(rollbackFor = Exception.class) public void updateFlag(Fruit fruit) { fruitDao.updateFlag(fruit.getId()); } @Override @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED) public void updateFlagOtherClz(final Fruit fruit) { FacadeServiceImpl.pool.execute(() -> { final BigDecimal price = fruit.getPrice(); if(price != null && price.compareTo(BigDecimal.TEN) > 0) { log.info("价格超过10元,更新标识"); updateFlag(fruit); int temp = 1/0; } }); } }
三、transaction
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
同一个类中调用,事务失效的解决方式:
声明式事务:强行经过代理
1:将事务方法分离到其他类中。
2:获取本对象的代理对象,再进行调用。具体操作如:
1):Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy="true"/>
在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),获取到xxxService的代理类,再调用事务方法,强行经过代理类,激活事务切面。
2): spring boot启动类上添加注解 @EnableAspectJAutoProxy(exposeProxy = true) 开启暴露代理类对象
AopContext.currentProxy()
3:注入自身,然后在用注入的bean调用自己的方法也可以
4:事务超时失效,如I/O造成的耗时,将与事务无关的操作变为线程异步执行。
5:使用编程式事务
https://www.cnblogs.com/wuer888/p/7840023.html
https://www.cnblogs.com/yixianyixian/p/8372832.html
事务注解Transactional在同一个类中调用的失效问题