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

4

将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.repository.CrudRepository接口来执行操作。
1)根据我的理解,将只读模式设置为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。否则它会出现异常,如评论所示。

注意:在某些帖子中建议检查PostgreSQL连接。

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

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

提前感谢。


请您能否发布一下堆栈跟踪信息? - Simon Martinelli
你好,每次我尝试添加堆栈跟踪时,它都会出现格式化代码错误,因此我暂时提供一个链接,谢谢:https://pastebin.com/a60pN4FV - dotmindlabs
从1.5升级到2.0是一个非常重要的进步。个人而言,由于没有足够的时间来完成所有必要的调整,我最终放弃了。 - Jagger
日志显示有一个事务正在创建,但没有使用以下语句关闭:no.app.application.security.UserDetailsService.getUserEntity(UserDetailsService.java:65)。您是否将其作为只读事务处理?同一线程继续并获取相同的事务,在上述方法调用期间未正确关闭。您能否分享更多关于该方法的详细信息? - Ajinkya
如果您能分享完整的代码,那真的会很有帮助。 - Ajinkya
2个回答

8
  1. 默认情况下,属性readOnly的值为false。因此,您不应使用@Transactional(readOnly = false),而应改用@Transactional
  2. 当您使用@Transactional标记某些方法或类时,Spring会创建该类的代理以注入Transaction Manager的逻辑。它使用实现了接口org.springframework.transaction.PlatformTransactionManager的bean。
  3. 在您的特定情况下,将创建一个org.springframework.orm.jpa.JpaTransactionManager的bean。
  4. Spring Boot使用Hibernate作为默认的JPA提供程序,因此最终所有事务逻辑都将影响Hibernate。例如,readOnly=true用于禁用在Hibernate中执行所有UPDATE操作的“dirty checking”机制。
  5. 默认情况下,当Spring Transaction Manager调用带有@Transactional注解的方法并且当前线程没有附加任何Session时,它将创建一个新的Hibernate Session(新的事务)。因此,当前线程中的所有后续调用都将使用相同的Session(和相同的事务),除非您更改了propagation属性。
  6. 所有这些意味着在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()是否真的打印了true吗? - Ajinkya
@Ajinkya 我更新了代码示例,现在更清晰了。 - Taras Boychuk

1

请查看 Spring Boot 2.0 迁移指南,了解 Gradle 并添加依赖管理插件:

Spring Boot’s Gradle plugin no longer automatically applies the dependency management plugin. Instead, Spring Boot’s plugin now reacts to the dependency management plugin being applied by importing the correct version of the spring-boot-dependencies BOM. This gives you more control over how and when dependency management is configured.

For most applications applying the dependency management plugin will be sufficient:

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

同时,您可以删除spring.datasource.type

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

请注意,最小的Hibernate版本为5.2

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接