java Sping Boot @Transactional不回滚数据库插入

r6l8ljro  于 2023-01-07  发布在  Java
关注(0)|答案(7)|浏览(172)

这里需要一些帮助,我不能理解为什么我的事务在异常事件中不回滚。
我会尽量把我的代码尽可能接近,因为它是在项目上(不能在互联网上共享)
这是我的服务

@Service
@SL4j
@Transactional(propagation = propagation.SUPPORTS, readOnly=true)
public class PublicationServiceImpl implements PublicationService{
    @Autowired 
    private PartnerRepo partnerRepo;
                
    @Autowired
    private FlowRepo flowRepo;

    @Autowired
    private PubRepo pubRepo;
                
    @Override
    @Transactional(propagation = propagation.REQUIRED, rollbackFor=Exception.class)
    public int save(Request request) {
    try{
                   int  pk_id_partner = partnerRepo.save(request);
                   int  pk_id_flow = flowRepo.save(request);
        
                   String publicationCode = generatePubCode(request);
                   int publicationCode= pubRepo.save(pk_id_partner, pk_id_flow, request);
        }
        catch(Exception e){
        log.error("Exception in saving");
    }
    return 0;
    }
}

这是我的存储库(示例1,所有3个存储库都遵循相同的编码标准)

@Repository
@Slf4j
public class PartnerRepo implemets PartnerRepo{
    @Autowired
    private NamedParamaterJDBCTemplate namedParamaterJDBCTemplate;
    //String Declarations .....

    private MapSqlParameterSource sqlParameterSource;

    @Override
    public int save(Request request){
        sqlParamatersSource = new MapSqlParameterSource();
        //sqlParamatersSource.addValue(.....)
        //sqlParamatersSource.addValue(.....)
        //sqlParamatersSource.addValue(.....)

        return executeQuery();
    }

    private int executeQuery(){
        try{
            keyHolder = new GenerateKeyHolder();
            namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource , kekHolder, new String[]{"pk_id"})
            return keyHolder.getKey().intValue();
        }catch(Exception e){
            log.error("Exception while saving");
            return 0;
        }
    }
}

所以问题是,考虑到generatePubCode(request);方法中有一个异常,理想情况下,因为我已经在类级别和方法级别使用了@Transactional,前面的2个repo事务()应该回滚吗?然而,它没有发生,即使在代码完成执行后,我也可以看到DB中的记录(Postgres DB v10)。
请帮助解决这个问题,我做了什么根本错误?
请让我知道,如果你需要进一步的信息,可能会有所帮助!
P. S:我已经尝试了@Transactional的所有排列,没有工作:只有在catch块中有这个才有效! TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();我想知道这是否是springBoot项目的正确方法
提前感谢您的帮助!
编辑:根据PublicationServiceSaverImpl.save()公开的建议
最好的药剂师,巴伽夫。

mnemlml8

mnemlml81#

在Spring中,有几种情况会破坏正常的事务
1.您的服务方式为private
1.您正在捕获并接受异常

private方法

你的PublicationServiceImplsave方法是private的事实基本上使得@Transactional在那个方法上是无用的。因为private方法不能被代理,所以没有事务将被应用。即使它是public,它也不会工作,因为你从同一个对象中调用该方法,因此该方法的事务性被应用。
要修复,请使您的方法public并从其他类调用save方法(或使调用save的实际方法具有正确的@Transactional)。
事实上,这是不工作的是由于类型的操作AOP正在使用,默认情况下,Spring将使用代理,这是一个缺点,使用代理为基础的AOP。
另一个让它与private方法一起工作的解决方案是切换到成熟的AspectJ,在编译时或加载时编织类,这两种方法都需要额外的设置,可能会很乏味。

捕获并吞下异常

在存储库和服务中都有一个try/catch块,每个块都捕获并吞下异常(它们被记录但不会被重新抛出)。
要使事务正常工作,它需要看到异常。您捕获并吞下它们的事实使事务方面看不到它们,因此不执行回滚,而是执行提交。对于事务方面,一切正常,因为没有异常。
要修复,请删除try/catch或重新引发异常。

11dmarpk

11dmarpk2#

由于Spring中代理的创建方式,注解通常不会对从同一个类调用的方法起作用。
它与@Transaction没有特别的关系,而是与您的方法是private从同一对象内调用这一事实有关。
请将方法设置为public,并将@Transactional方法移动到标有@Service的单独类中,然后从类示例外部调用它

@Service
  public class PublicationServiceSaverImpl {

     @Transactional
     **public** int save(Request request) {
      ...
     }
  }

必须从类PublicationServiceSaverImpl的外部调用保存方法,可能是从PublicationServiceImpl调用。

qlvxas9a

qlvxas9a3#

如果要使用@Transactional,则方法PublicationServiceImpl.save必须为public
根据Spring文件:
在Spring的标准配置下使用事务代理时,您应该只对具有公共可见性的方法应用@Transactional注解。如果您确实使用@Transactional注解了受保护的、私有的或包可见的方法,则不会引发任何错误,但注解的方法不会显示配置的事务设置。

sulc1iza

sulc1iza4#

首先:公开你的方法。
第二:你必须抛出异常。如果你捕获了它而没有重新抛出,你怎么能期望事务处理知道发生了错误,然后回滚呢?
您有两种选择:抛出Exception而不是捕获它,或者捕获,做一些进一步的处理,然后重新抛出它。
因此,在存储库中,只需添加一个throws关键字,然后在log语句之后重新抛出异常:

public int executeQuery() throws Exception {
  try {
    keyHolder = new GenerateKeyHolder();
    namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource, kekHolder, new String[] {
      "pk_id"
    })
    return keyHolder.getKey().intValue();
  } catch(Exception e) {
    log.error("Exception while saving");
    throw e;
  }
}

现在,为了你的服务:
示例1-使用throws关键字传播已检查异常:

@Override
@Transactional(propagation = propagation.REQUIRED, rollbackFor = Exception.class)
public int save(Request request) throws Exception {
  int pk_id_partner = partnerRepo.save(request);
  int pk_id_flow = flowRepo.save(request);

  String publicationCode = generatePubCode(request);
  int publicationCode = pubRepo.save(pk_id_partner, pk_id_flow, request);
  return 0;
}

示例2-捕获并将其作为RuntimeException重新抛出,该异常未被选中。

@Override
@Transactional(propagation = propagation.REQUIRED)
public int save(Request request) {
  try {
    int pk_id_partner = partnerRepo.save(request);
    int pk_id_flow = flowRepo.save(request);

    String publicationCode = generatePubCode(request);
    int publicationCode = pubRepo.save(pk_id_partner, pk_id_flow, request);
  } catch(Exception ex) {
    throw new RuntimeException(ex);
  }
  return 0;
}

注意,第二个示例不需要@TransactionalrollbackFor参数,默认情况下,如果发生未检查的异常,事务将回滚,因此在RuntimeException的情况下不需要显式使用rollbackFor

jmp7cifd

jmp7cifd5#

如果解决方案不起作用,则必须进行另一个验证。它是验证数据库表是否允许回滚。为此,引擎必须位于InnoDB中,而不是MyISAM和其他位置。

lnvxswe2

lnvxswe26#

在我的例子中,在Application类上添加@EnableTransactionManagement注解解决了这个问题

blpfk2vs

blpfk2vs7#

提供@事务性(传播=传播.REQUIRED_NEW,而不是@事务性(传播=传播.REQUIRED
如果使用后者,它将使用父事务边界,该边界位于类级别。
而且您不需要显式地声明rollbackFor= Exception.class。
并将私有更改为公共
试试这个

相关问题