Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如果B出错,请回滚A。spring引导,jdbctemplate_Java_Oracle_Spring Boot_Jdbctemplate_Spring Transactions - Fatal编程技术网

Java 如果B出错,请回滚A。spring引导,jdbctemplate

Java 如果B出错,请回滚A。spring引导,jdbctemplate,java,oracle,spring-boot,jdbctemplate,spring-transactions,Java,Oracle,Spring Boot,Jdbctemplate,Spring Transactions,我有一个方法“databaseChanges”,它以迭代的方式调用2个操作:a、BA‘第一’,B‘最后。 “A”和“B”可以是我的持久性存储Oracle Database 11g中的CCreate、UpdateDelete功能 比方说 “A”更新表用户中的记录,属性为zip,其中id=1 “B”在表中插入记录 场景:调用databaseChanges方法时,“A”操作并更新记录B'操作并尝试插入一条记录,发生了一些情况,抛出了一个异常,异常冒泡到databaseChanges方法 预期:“A”和

我有一个方法“databaseChanges”,它以迭代的方式调用2个操作:a、BA‘第一’,B‘最后。 “A”和“B”可以是我的持久性存储Oracle Database 11g中的CCreate、UpdateDelete功能

比方说

“A”更新表用户中的记录,属性为zip,其中id=1

“B”在表中插入记录

场景:调用databaseChanges方法时,“A”操作并更新记录B'操作并尝试插入一条记录,发生了一些情况,抛出了一个异常,异常冒泡到databaseChanges方法

预期:“A”和“B”没有改变任何东西。“A”执行的更新将被回滚。”B'没有改变什么,嗯。。。有一个例外

实际:“A”更新似乎未回滚B'没有改变什么,嗯。。。有一个例外


一些代码

compile("org.springframework:spring-tx")
如果我有连接,我会做如下事情:

private void databaseChanges(Connection conn) {
   try {
          conn.setAutoCommit(false);
          A(); //update.
          B(); //insert
          conn.commit();
   } catch (Exception e) { 
        try {
              conn.rollback();
        } catch (Exception ei) {
                    //logs...
        }
   } finally {
          conn.setAutoCommit(true);
   }
}
问题:我没有连接(请参阅问题后的标签)

我试图:

@Service
public class SomeService implements ISomeService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate npjt;

    @Transactional
    private void databaseChanges() throws Exception {   
        A(); //update.
        B(); //insert
    }
}

我的AppConfig类:

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

@Configuration
public class AppConfig {    
    @Autowired
    private DataSource dataSource;

    @Bean
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
        return new NamedParameterJdbcTemplate(dataSource);
    }   
}

“A”进行更新。从“B”引发异常。“A”所做的更新未回滚

从我阅读的内容中,我了解到我没有正确使用@Transactional。 我阅读并尝试了几篇博客文章和stackverflow问答,但都没有成功解决我的问题

有什么建议吗


编辑

有一个方法调用databaseChanges()方法

哪个方法应该用@Transactional注释


更改()?databaseChanges()?

您提供的第一个代码是针对UserTransactions的,即应用程序必须执行事务管理。通常,您希望容器处理这个问题,并使用@Transactional注释。我认为您案例中的问题可能是,您在私有方法上有注释。我会将注释移动到类级别

@Transactional
public class MyFacade {

public void databaseChanges() throws Exception {   
    A(); //update.
    B(); //insert
}
然后它应该正确地回滚。你可以在这里找到更多细节 试试这个:

@TransactionManagement(TransactionManagementType.BEAN)
public class MyFacade {

@TransactionAttribute(TransactionAttribute.REQUIRES_NEW)
public void databaseChanges() throws Exception {   
    A(); //update.
    B(); //insert
}

spring中的
@Transactional
注释通过将对象包装在代理中来工作,代理又将使用
@Transactional
注释的方法包装在事务中。由于该注释无法在私有方法上工作(如您的示例中),因为私有方法不能被继承=>它们不能被包装(如果将声明性事务与一起使用,则不适用下面与代理相关的警告)

下面是关于
@Transactional
spring magic如何工作的基本解释

你写道:

class A {
    @Transactional
    public void method() {
    }
}
但这是注入bean时实际得到的结果:

class ProxiedA extends A {
   private final A a;

   public ProxiedA(A a) {
       this.a = a;
   }

   @Override
   public void method() {
       try {
           // open transaction ...
           a.method();
           // commit transaction
       } catch (RuntimeException e) {
           // rollback transaction
       } catch (Exception e) {
           // commit transaction
       }
   }
} 
这有局限性。它们不使用
@PostConstruct
方法,因为它们是在代理对象之前调用的。即使您正确配置了所有事务,默认情况下,事务也只在未检查的异常上回滚。如果您需要回滚某些已检查的异常,请使用
@Transactional(rollboor={CustomCheckedException.class})

我知道另一个经常遇到的警告:


@Transactional
方法只有在您将其称为“从外部”时才起作用,在下面的示例中,
b()
将不会包装在事务中:

class X {
   public void a() {
      b();
   }

   @Transactional
   public void b() {
   }
}
这也是因为
@Transactional
通过代理对象来工作。在上面的示例中,
a()
将调用
X.b()
而不是一个增强的“spring代理”方法
b()
,因此将不存在事务。作为一种解决方法,您必须从另一个bean调用
b()

当您遇到这些警告并且无法使用建议的解决方法(使方法非私有或从另一个bean调用
b()
)时,您可以使用
TransactionTemplate
而不是声明性事务:

public class A {
    @Autowired
    TransactionTemplate transactionTemplate;

    public void method() {
        transactionTemplate.execute(status -> {
            A();
            B();
            return null;
        });
    }

...
} 
更新

compile("org.springframework:spring-tx")
使用上述信息回答OP更新的问题

哪个方法应该用@Transactional注释: 更改()?数据库更改()


确保
changes()
是从bean的“外部”调用的,而不是从类本身以及在实例化上下文之后调用的(例如,这不是
afterPropertieSet()
@PostConstruct
带注释的方法)。了解spring在默认情况下仅对未检查的异常回滚事务(请尝试在rollbackFor checked exceptions列表中更加具体)

您似乎缺少的是一个
TransactionManager
TransactionManager
的目的是能够管理数据库事务。有两种类型的事务,编程事务和声明事务。您所描述的是对通过注释的声明性事务的需求

因此,您需要为您的项目准备以下内容:

Spring事务依赖项(以Gradle为例)

在Spring引导配置中定义事务管理器

像这样的

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource)
{
    return new DataSourceTransactionManager(dataSource);
}
您还需要添加
@EnableTransactionManagement
注释(不确定这在较新版本的spring boot中是否是免费的)

@EnableTransactionManagement
public class AppConfig {
...
}
添加@Transactional

在这里,您将为要参与事务的方法添加
@Transactional
注释

@Transactional
public void book(String... persons) {
    for (String person : persons) {
        log.info("Booking " + person + " in a seat...");
        jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
    }
};

注意,此方法<强>应该是public <强>而不是私有的。您可能需要考虑在公共方法调用<代码>数据库更改()/<代码> > < /p> 还有一些高级主题是关于

@Transactional
应该去哪里以及它的行为方式,因此最好先做一些工作,然后再稍后探索这个领域:)

在所有这些就绪(依赖项+transactionManager配置+注释)之后,事务应该相应地工作。

@Transactional public void book(String... persons) { for (String person : persons) { log.info("Booking " + person + " in a seat..."); jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person); } };
@Service
public class SomeService implements ISomeService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate npjt;

    @Transactional(rollbackFor = Exception.class)
    private void databaseChanges() throws Exception {   
        A(); //update
        B(); //insert
    }
}
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor = Exception.class)
@Documented
public @interface CustomTransactional {  
}
@Service
public class SomeService implements ISomeService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate npjt;

    @CustomTransactional
    private void databaseChanges() throws Exception {   
        A(); //update
        B(); //insert
    }
}