You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Spring Boot JPA报错:无法在只读事务中执行INSERT问题

问题解决结论

可以通过Java代码层面调整解决该问题,该报错是多数据源场景下事务配置错误导致,无需修改数据库服务端配置。

报错详情:org.springframework.orm.jpa.JpaSystemException : could not execute statement ; nested exception is org.hibernate.exception.GenericJDBCException : Could not execute statement with root cause org.postgresql.util.PSQLException: ERROR cannot execute INSERT in a read-only transaction

错误根因

该报错本质是执行写操作的事务被标记为只读属性,多数据源场景下常见触发原因有3种:

  • 多数据源对应的事务管理器配置错误,混用不同数据源的事务管理器
  • 写操作方法/所属类上错误标注了@Transactional(readOnly = true)注解
  • 数据源默认配置了只读的连接属性
具体解决步骤

1. 配置多数据源独立的事务管理器

每个数据源必须对应独立的PlatformTransactionManager,且在@EnableJpaRepositories注解中绑定对应事务管理器,示例配置如下:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = "com.yourproject.repository.second", // 对应数据源的repository包路径
    entityManagerFactoryRef = "secondEntityManagerFactory",
    transactionManagerRef = "secondTransactionManager" // 绑定对应事务管理器
)
public class SecondDataSourceConfig {

    // 数据源、EntityManagerFactory配置省略

    @Bean
    public PlatformTransactionManager secondTransactionManager(
        @Qualifier("secondEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        // 强制关闭事务默认只读配置
        transactionManager.setDefaultReadOnly(false);
        return transactionManager;
    }
}

2. 修正事务注解配置

写操作对应的方法/类上的@Transactional注解必须指定对应数据源的事务管理器,同时明确设置只读属性为false:

// 写方法注解示例
@Transactional(transactionManager = "secondTransactionManager", readOnly = false)
public void saveData(DataEntity data) {
    dataRepository.save(data);
}

如果类上标注了全局的@Transactional(readOnly = true),写方法的注解配置会覆盖类上的全局配置。

3. 强制设置连接读写属性

如果以上配置完成后仍存在问题,可以在数据源配置中添加连接初始化SQL,每次获取连接时强制设置为读写模式:

// Hikari连接池示例
@Bean
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource secondDataSource() {
    HikariDataSource dataSource = new HikariDataSource();
    // 连接初始化时执行SQL关闭只读
    dataSource.setConnectionInitSql("SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE;");
    return dataSource;
}

如果使用Druid连接池,对应配置参数为connectionInitSqls,传入相同SQL即可。

内容的提问来源于stack exchange,提问作者oxygenan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 03:30:03