【问题标题】:Spring database transaction not committing data in DbSpring数据库事务未在Db中提交数据
【发布时间】:2019-04-28 09:55:11
【问题描述】:

我创建了一个带有 spring boot 的项目。我有 hikariConfig 来创建用于连接池的数据源,并将 autocommmit 属性设置为 false。使用在 DataSourceTransactionManager 的 @Transaction 注释的方法中运行的 jdbcTemplate 进行批量插入。程序执行后,我无法看到数据被插入到 Db 中。如果我在 hikariconfig 中使自动提交为真,它就可以正常工作。

@SpringBootApplication

public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@Component

@EnableTransactionManagement

public class DataSourceConfig {

    @Bean (name = "dateSourceForSqlServer")

    public DataSource dataSourceForSqlServer () {

        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setConnectionTimeout(10000L);
        hikariConfig.setIdleTimeout(10000L);
        hikariConfig.setMinimumIdle(1);
        hikariConfig.setMaximumPoolSize(1);
       hikariConfig.setMaxLifetime(600000L);
        hikariConfig.setConnectionTestQuery("select 1");
        hikariConfig.setValidationTimeout(4000L);


        hikariConfig.setJdbcUrl("jdbc:sqlserver://localhost:1433;database=invt_mgmt");
        hikariConfig.setUsername("sa");
        hikariConfig.setPassword("sql_server_pass_123");

         hikariConfig.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");


    hikariConfig.setAutoCommit(false);

        return  new HikariDataSource(hikariConfig);
    }

    @Bean (name = "jdbcTemplateForSqlServer")
    public JdbcTemplate jdbcTemplateForSqlServer () {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSourceForSqlServer());

        return jdbcTemplate;
    }

    @Primary
    @Bean(name = "invtMgmtTxMangerForSqlServer")
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSourceForSqlServer());
        return manager;
    }

}


@Component

public class startBean {

    @Autowired
    private Business Business;

    @PostConstruct
    public void startApp() throws SQLException {
        Business.insertContainerHierarchy();
        Business.insertContainerHierarchy();

    }
}

@Component

 class public Business {

    @Autowired
    @Qualifier("jdbcTemplateForSqlServer")
    private JdbcTemplate jdbcTemplateForSqlServer;

    String insertIntStudent = "INSERT INTO student (id, name) Values(?, ?)";

    @Transactional(value = "invtMgmtTxMangerForSqlServer")
    public void insertContainerHierarchy () throws SQLException {

      System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
      System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());


        Date start = new Date();

        for (int i = 0; i < 500; i++) {
            System.out.println(i);
            jdbcTemplateForSqlServer.batchUpdate(insertIntStudent, new BatchPreparedStatementSetter() {

                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                   ps.setInt(1, i);
                   ps.setString(2, String.valueOf(i));
                }

                @Override
                public int getBatchSize() {
                    return 1000;
                }
            });
        }
        System.out.println(new Date().getTime() - start.getTime());
    } 

}

我使用了TransactionSynchronizationManager.isActualTransactionActive(),它在方法执行时返回true。

第一季度。为什么数据没有被插入,Transaction 应该在方法执行后自动提交?

第二季度。如果使用spring事务,数据库连接自动提交值会有什么不同吗?

第三季度。目前我如何能够在自动提交设置为 true 的情况下插入?

【问题讨论】:

  • 一目了然,示例代码看起来有点奇怪。您有一个包含 500 次迭代的循环,但您使用的是批处理大小为 1000 的 BatchPreparedStatementSetter(这意味着 setValues 将被调用 1000 次)。您真的要插入 500 x 1000 = 500,000 行吗?并且当 for 循环迭代时,下一组插入可能会导致 student.id 上的主键违规。
  • 对 id 或 name 没有唯一约束。它是一个虚拟项目,我正在寻找批处理性能,所以是 500 * 1000 行。

标签: java sql-server spring spring-boot spring-transactions


【解决方案1】:

您正试图从@PostConstruct 方法中调用事务包装代理。对于该 bean,所有初始化可能都已完成,但对于上下文的其余部分则不一定。并非所有代理都可以在此时设置。

我建议实现ApplicationListener&lt;ContextRefreshedEvent&gt; 接口以触发该类中的任何数据创建。这将确保在设置整个上下文后调用它:

@Component
public class OnContextInitialized implements
  ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private Business Business;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        Business.insertContainerHierarchy();
        Business.insertContainerHierarchy();
    }
}

【讨论】:

  • 但是 System.out.println(TransactionSynchronizationManager.isActualTransactionActive()); 是真实的
  • 看起来事务已经开始,不是标志提示的吗。
猜你喜欢
  • 2014-05-29
  • 1970-01-01
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多