从 Spring Boot 1.5 升级到 2.0 - 无法在只读事务中执行 UPDATE

Posted

技术标签:

【中文标题】从 Spring Boot 1.5 升级到 2.0 - 无法在只读事务中执行 UPDATE【英文标题】:Upgrading from Spring Boot 1.5 to 2.0 - cannot execute UPDATE in a read-only transaction 【发布时间】:2019-10-29 03:25:58 【问题描述】:

将 Spring Boot 1.5 更新到 2.1.5

当尝试执行操作 repository.save(entity) 时,会出现以下错误:

Caused by: com.impossibl.postgres.jdbc.PGSQLSimpleException: cannot execute UPDATE in a read-only transaction

我们使用 org.springframework.data.repositoryCrudRepository 接口来执行操作。

1) @Transactional(readOnly = false),据我了解,将只读模式设置为 false 仅作为对子层的提示,我如何检查和更改其他层?

@Service
public class ServiceImpl

    private final Repository repository;

    @Autowired
    public ServiceImpl(Repository repository) 
        this.repository = repository;
    
@Transactional(readOnly = false)
public void operation(Entity entity)
    repository.save(entity);

而存储库是

public interface Repository extends CrudRepository<Entity, UUID>

    @Query("select e from Entity e where lower(u.name) = lower(?1)")
    Entity findByName(String name);



build.gradle
------------

`dependencies 
    classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.5.RELEASE")

`

```runtime("org.springframework.boot:spring-boot-properties-migrator")
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-jersey")
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.springframework.boot:spring-boot-starter-jetty")
    compile("org.springframework.boot:spring-boot-starter-mail")
    compile("org.springframework.boot:spring-boot-starter-actuator")
    compile("org.quartz-scheduler:quartz:2.3.1")
    compile("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
    compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
    compile("com.fasterxml.woodstox:woodstox-core:5.2.1")
    compile("org.glassfish.jersey.media:jersey-media-multipart:2.28")
    compile("net.java.dev.msv:msv-core:2013.6.1")
    compile("com.impossibl.pgjdbc-ng:pgjdbc-ng:0.8.2")
    compile('org.apache.commons:commons-lang3:3.9')
    compile('commons-io:commons-io:2.6')
    compile('org.apache.commons:commons-compress:1.18')
    compile('org.apache.poi:poi-ooxml:4.1.0')
    compile('org.apache.xmlbeans:xmlbeans:3.1.0')
    compile('org.mitre.dsmiley.httpproxy:smiley-http-proxy-servlet:1.10')
    compile('com.monitorjbl:xlsx-streamer:2.1.0')
    compile('com.zaxxer:HikariCP:3.3.1')

application.properties

spring.datasource.driverClassName=com.impossibl.postgres.jdbc.PGDriver

spring.datasource.url=
spring.datasource.username=
spring.datasource.password=


spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.idle-timeout=10000

 # Set auto-commit = false, otherwise - Caused by: java.sql.SQLException: 
  Clobs require connection to be in manual-commit mode... 


spring.datasource.hikari.auto-commit=false

logging.level.ROOT=INFO
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=DEBUG

一个重要的事情是我在 Hikari 中将 Auto-Commit 添加为 false,否则它会失败并出现异常,因为它可以在评论中看到。

注意:在某些线程中建议检查 postgres 连接

    show default_transaction_read_only;
     default_transaction_read_only 
    -------------------------------
     off

    SELECT pg_is_in_recovery();
     pg_is_in_recovery 
    -------------------
     f

提前致谢。

【问题讨论】:

你能发布堆栈跟踪吗? 您好,每次我尝试添加堆栈跟踪时都会出现格式代码错误,所以我同时提供链接,谢谢:pastebin.com/a60pN4FV 从 1.5 升级到 2.0 是向前迈出的一大步。就个人而言,我最终放弃了,因为没有时间进行所有需要的调整。 日志显示有一个事务被创建并且没有被这个语句关闭:no.app.application.security.UserDetailsS​​ervice.getUserEntity(UserDetailsS​​ervice.java:65) 你有这个作为只读事务吗?同一线程继续并获得在上述方法调用期间未正确关闭的同一事务。您能否分享有关该方法的更多详细信息? 如果你能分享完整的代码,那真的很有帮助。 【参考方案1】:

请参阅Spring Boot 2.0 Migration Guide 了解 Gradle 并添加依赖管理插件:

Spring Boot 的 Gradle 插件不再自动应用依赖管理插件。相反,Spring Boot 的插件现在通过导入正确版本的 spring-boot-dependencies BOM 来响应正在应用的依赖管理插件。这让您可以更好地控制依赖管理的配置方式和时间。

对于大多数应用程序来说,应用依赖管理插件就足够了:

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management' // <-- add this to your build.gradle

你也可以删除spring.datasource.type

如果您使用 spring.datasource.type 在基于 Tomcat 的应用程序中强制使用 Hikari,您现在可以删除该覆盖。

另请注意,最低 Hibernate 版本为 5.2

我还看到你添加了spring-boot-properties-migrator,请注意,在完成迁移调整后应该将其删除

完成迁移后,请确保从项目的依赖项中删除此模块。

【讨论】:

【参考方案2】:
    readOnly 属性默认为false,所以你不应该使用@Transactional(readOnly = false),而是使用@Transactional。 当您使用@Trasnacional 标记某些方法或类时,Spring 会创建该类的代理来注入 Transaction Manager 的逻辑。它使用实现接口org.springframework.transaction.PlatformTransactionManager的bean 在您的具体情况下,将创建一个 org.springframework.orm.jpa.JpaTransactionManager 的 bean。 Spring Boot 使用 Hibernate 作为默认的 JPA 提供程序,因此最终所有事务逻辑都会影响 Hibernate。例如。 readOnly = true 用于禁用在Hibernate 中执行所有更新操作的“脏检查”机制。 默认情况下,Spring Transaction Manager 在调用带有@Transactional 而没有Session的方法标记时会创建一个新的Hibernate Session(新转换) em> 附加到当前线程。因此,当前线程中的所有以下调用都将使用相同的Session(和相同的事务)。除非您更改 propagation 属性。 这一切都意味着事务的配置是在Spring第一次调用@Transactional方法时设置的,并且这些配置用于同一线程中的所有方法调用。参见代码示例:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
public class ServiceA 

    @Transactional(readOnly = true)
    public void a() 
        boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
        System.out.println(isReadOnly);
    

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ServiceB 
    private final ServiceA serviceA;

    public ServiceB(ServiceA serviceA) 
        this.serviceA = serviceA;
    

    @Transactional
    public void b() 
        serviceA.a();
    

serviceA.a() 将打印 true serviceB.b() 将打印 false

【讨论】:

从 a 调用 b 与私有方法调用一样好,并且它不是通过 spring 代理调用的,因此 b() 上标记的 @Transactional 属性不会生效。你检查 txService.b() 是否真的打印正确? @Ajinkya 我更新了代码示例,现在更清晰了。

以上是关于从 Spring Boot 1.5 升级到 2.0 - 无法在只读事务中执行 UPDATE的主要内容,如果未能解决你的问题,请参考以下文章

从 Spring Boot 1.5 升级时为 Spring Boot 2.0 acuator 框架配置安全性

Spring Boot 2.0干货系列:Spring Boot1.5X升级到2.0指南

雪球 Spring Boot 2.0 升级经验谈

多模块 Gradle 项目 - 从 Spring-Boot 1.5 迁移到 2.1

给大家聊一聊云收藏从 Spring Boot 1.0 升级到 2.0 所踩的坑

在升级到 Spring Boot 2.0 时,得到 UnsatisfiedDependencyException