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:使用编程式事务

 

Spring事务详细解释,满满的都是干货!

 

 Spring事务管理(详解+实例)

https://www.cnblogs.com/wuer888/p/7840023.html

https://www.cnblogs.com/yixianyixian/p/8372832.html

 

Spring_第五章【Spring的事务代码实现】

深入理解 Spring 事务原理

@Transactional失效原因分析

事务注解Transactional在同一个类中调用的失效问题

 

 

 

原文地址:https://www.cnblogs.com/foolash/p/12165254.html