Jooq自定义删除方法抛出ERROR:无法在只读事务中执行DELETE

问题描述 投票:0回答:1

我使用Springboot3和Jooq来创建我的项目并定义自定义删除方法

deleteByUsername
遵循jooq删除指南


@Repository
public class UserRepository extends UserDao {

  @Autowired
  public UserRepository(Configuration configuration) {
    super(configuration);
  }

  /* --- some query method can run sucessful in run and test---
   ........
  */

  public void deleteByUsername(String username) {
    ctx().delete(USER).where(USER.USERNAME.eq(username)).execute();
  }
}

UserRepository 扩展了 UserDao,因此

ctx()
返回一个 DSLContext 实例。

@Repository
public class UserDao extends AbstractSpringDAOImpl<UserRecord, jooq.tables.pojos.User, Long> {

    /**
     * Create a new UserDao without any configuration
     */
    public UserDao() {
        super(User.USER, jooq.tables.pojos.User.class);
    }

    /**
     * Fetch records that have <code>id IN (values)</code>
     */
    public List<jooq.tables.pojos.User> fetchById(Long... values) {
        return fetch(User.USER.ID, values);
    }

    /**
     * Fetch a unique record that has <code>id = value</code>
     */
    public jooq.tables.pojos.User fetchOneById(Long value) {
        return fetchOne(User.USER.ID, value);
    }

    .....more
}

UserDao 是由 jooq 代码生成插件生成的,如果你设置


generate.apply {
   isDeprecated = false
   isRecords = true
   isImmutablePojos = false
   isFluentSetters = true
   isDaos = true // will create dao layer by jooq
   isSpringDao = true
   isSpringAnnotations = true
}

所有*Dao都是扩展DAOImpl,ctx()是dao层获取DSLContext的方法

public abstract class DAOImpl<R extends UpdatableRecord<R>, P, T> implements DAO<R, P, T> {

    private final Table<R>     table;
    private final Class<P>     type;
    private RecordMapper<R, P> mapper;
    private Configuration      configuration;


    /**
     * Inject a configuration.
     * <p>
     * This method is maintained to be able to configure a <code>DAO</code>
     * using Spring. It is not exposed in the public API.
     */
    public /* non-final */ void setConfiguration(Configuration configuration) {
        this.mapper = ((FieldsImpl<R>) table.recordType()).mapper(this.configuration = Tools.configuration(configuration), type);
    }

    public /* non-final */ DSLContext ctx() {
        return configuration().dsl();
    }

}

问题

当我在 springboot 测试中运行

deleteByUsername
时,它失败了,并在下面抛出异常。

但是

jooqDeleteRunSuccess
运行成功。
userRepository.delete
中定义的
jooqDeleteRunSuccess
方法是jooq生成的dao层的方法,不是自定义方法。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class SignE2ETest {

  @Autowired private WebTestClient webClient;

  @Autowired private UserRepository userRepository;

  @Test
  void jooqDeleteRunSuccess() {
    User stubUser = new User();
    stubUser.setUsername("test_5fab32c22a3e");
    stubUser.setPassword("test_eab28b939ba1");
    userRepository.insert(stubUser);
    userRepository.delete(userRepository.fetchOneByUsername("test_5fab32c22a3e"));
  }

  @Test
  void myDeleteRunFailed() {
    User stubUser = new User();
    stubUser.setUsername("test_5fab32c22a3e");
    stubUser.setPassword("test_eab28b939ba1");
    userRepository.insert(stubUser);
    userRepository.deleteByUsername("test_5fab32c22a3e");
  }
}
org.jooq.exception.DataAccessException: SQL [delete from "mjga"."user" where "mjga"."user"."username" = ?]; ERROR: cannot execute DELETE in a read-only transaction
 at org.jooq_3.18.6.POSTGRES.debug(Unknown Source)
 at org.jooq.impl.Tools.translate(Tools.java:3470)
 at org.jooq.impl.Tools.translate(Tools.java:3458)
 at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:801)
 at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:360)
 at org.jooq.impl.AbstractDelegatingQuery.execute(AbstractDelegatingQuery.java:115)
 at com.mjga.repository.UserRepository.deleteByUsername(UserRepository.java:61)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.base/java.lang.reflect.Method.invoke(Method.java:568)
.....

问题

正如你所看到的,没有明确的配置将postgresql设置为只读模式,我不知道为什么会发生这个只读异常?

为什么

myDeleteRunFailed
会抛出 READ-ONLY 异常,而
jooqDeleteRunSuccess
不会?

如何解决?

谢谢!

环境

  • springboot-3.1.2
  • jooq-3.18.6
  • postgresql-15.4
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-jooq")
    implementation("org.springframework.boot:spring-boot-starter-mail")
    implementation("org.springframework.boot:spring-boot-starter-quartz")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-websocket")
    implementation("org.springframework.boot:spring-boot-starter-aop")
    implementation("org.apache.commons:commons-lang3:3.13.0")
    implementation("org.apache.commons:commons-collections4:4.4")
    implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")
    implementation("org.jooq:jooq-codegen:3.18.6")
    implementation("org.jooq:jooq-meta:3.18.6")
    implementation("com.auth0:java-jwt:4.4.0")
    implementation("org.testcontainers:junit-jupiter:1.19.0")
    implementation("org.testcontainers:postgresql:1.19.0")
    implementation("org.testcontainers:testcontainers-bom:1.19.0")
    runtimeOnly("org.postgresql:postgresql")
    compileOnly("org.projectlombok:lombok")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    developmentOnly("org.springframework.boot:spring-boot-docker-compose")
    testImplementation("org.springframework.boot:spring-boot-testcontainers:3.1.2")
    testImplementation("org.springframework.boot:spring-boot-docker-compose")
    testImplementation("org.springframework.boot:spring-boot-starter-webflux")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.security:spring-security-test")
    jooqGenerator("org.postgresql:postgresql")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    annotationProcessor("org.projectlombok:lombok")
}

services:
  postgres:
    container_name: postgres
    image: 'postgres:15.4'
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - ${DATABASE_PORT}:5432
    volumes:
      - ./db.d/init:/docker-entrypoint-initdb.d
      - ${DATABASE_STORE}:/var/lib/postgresql/data
    restart: on-failure

我已经检查了我的数据库只读状态

SHOW default_transaction_read_only;
SHOW transaction_read_only;

off
off
java postgresql spring-boot jooq spring-boot-test
1个回答
0
投票

这是我第一次在 stackoverflow 上回答问题。

我的英语不好,所以我希望你能明白我说的话


错误“无法在只读事务中执行DELETE”表示您正在尝试在只读事务中执行DELETE操作,该操作不允许修改数据库。

所以我猜jooq方法“jooqDeleteRunSuccess”在执行DELETE操作之前启动了一个可写事务

但是你的个人方法“myDeleteRunFailed”只获取了DSLContext实例,但没有发起可写事务

© www.soinside.com 2019 - 2024. All rights reserved.