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

Oracle Test Container抛ORA-01918错误,如何创建指定数据库用户?

解决Testcontainers Oracle容器中ORA-01918用户不存在错误

问题场景

我是Oracle Container新手,Spring Boot应用有两个数据源,在JUnit5集成测试中用Testcontainers创建两个Oracle容器实例。执行建表SQL脚本时抛出SQLSyntaxErrorException,错误信息为ORA-01918: user does not exist。

相关代码

容器初始化抽象类

@Slf4j
public class AbstractContanierBaseTest {

    static final OracleContainer ORACLE_OPIOWNER_CONTANINER;
    static final OracleContainer ORACLE_FIRMAOWNER_CONTANINER;

    static {
        ORACLE_OPIOWNER_CONTANINER = new OracleContainer("gvenzl/oracle-xe:21-slim");
        ORACLE_OPIOWNER_CONTANINER
                .withUsername("opiowner")
                .withPassword("password")
                .withDatabaseName("OPIOWNER")
                .withExposedPorts(1521)
                .withLogConsumer(new Slf4jLogConsumer(log))
                .withEnv("ORACLE_DATABASE", "OPIOWNER")
                .withEnv("ORACLE_PASSWORD", "password")
                .withEnv("APP_USER", "opiowner")
                .withEnv("APP_USER_PASSWORD", "password")
                .usingSid();
        ORACLE_OPIOWNER_CONTANINER.start();

        System.setProperty("spring.datasource.driver-class-name", ORACLE_OPIOWNER_CONTANINER.getDriverClassName());
        System.setProperty("spring.datasource.url", ORACLE_OPIOWNER_CONTANINER.getJdbcUrl());
        System.setProperty("spring.datasource.username", ORACLE_OPIOWNER_CONTANINER.getUsername());
        System.setProperty("spring.datasource.password", ORACLE_OPIOWNER_CONTANINER.getPassword());

        ORACLE_FIRMAOWNER_CONTANINER = new OracleContainer("gvenzl/oracle-xe:11-slim");
        ORACLE_FIRMAOWNER_CONTANINER
                .withUsername("firmaowner")
                .withPassword("password")
                .withDatabaseName("FIRMAOWNER")
                .withLogConsumer(new Slf4jLogConsumer(log))
                .withEnv("ORACLE_PASSWORD", "password")
                .withEnv("APP_USER", "firmaowner")
                .withEnv("APP_USER_PASSWORD", "password")
                .usingSid();
        ORACLE_FIRMAOWNER_CONTANINER.start();

        System.setProperty("spring.second-datasource.url", ORACLE_FIRMAOWNER_CONTANINER.getJdbcUrl());
        System.setProperty("spring.second-datasource.username", ORACLE_FIRMAOWNER_CONTANINER.getUsername());
        System.setProperty("spring.second-datasource.password", ORACLE_FIRMAOWNER_CONTANINER.getPassword());

    }
}

JUnit测试的SQL执行注解

@Sql(config = @SqlConfig(transactionManager = "opiownerTransactionManager"), scripts = {"classpath:scripts/ddl/Recall_create_table_opiConfluenzaDati.sql", "classpath:scripts/dml/Recall_opiConfluenzaDati_script.sql"})

建表SQL脚本

CREATE TABLE opiowner.OPI_CONFLUENZA_DATI
(
    "OPI_ANNO_EMISSIONE_TITOLO" NUMBER(4,0),
    "OPI_TIPOLOGIADISPOSIZIONE" VARCHAR2(11),
    "OPI_DATADISPOSIZIONE" DATE,
    "OPI_ID_DISPOSIZIONE" NUMBER(10,0) NOT NULL,
    "OPI_NUM_TITOLO" NUMBER(9,0),
    "OPI_PROG_QUOTA" NUMBER(15,0),
    "OPI_TIPO_COD_BENEF" VARCHAR2(2),
    "OPI_CODICE_BENEF" VARCHAR2(16),
    "OPI_TIPO_RECORD" VARCHAR2(11),
    "OPI_SPECIE_TITOLO" VARCHAR2(2),
    "OPI_TIPO_SPESA" VARCHAR2(5),
    "OPI_COD_ENTE" VARCHAR2(15),
    "OPI_IMPORTO" NUMBER(15,3),
    "OPI_NOME_FILE" VARCHAR2(100),
    "OPI_ORDINANTE" VARCHAR2(100)
);

ALTER TABLE opiowner.OPI_CONFLUENZA_DATI
ADD CONSTRAINT OPI_CONFLUENZA_DATI_PK PRIMARY KEY ("OPI_TIPOLOGIADISPOSIZIONE", "OPI_DATADISPOSIZIONE", "OPI_ID_DISPOSIZIONE");

问题原因

你使用的gvenzl/oracle-xe镜像有自己的环境变量初始化逻辑,同时混用了Testcontainers的withUsername/withPassword方法和镜像的APP_USER环境变量,导致用户没有被正确创建;另外,SQL脚本中指定opiowner.前缀,但连接用户可能没有对应权限或用户本身不存在。

解决方案

方案1:正确配置镜像环境变量创建用户

gvenzl/oracle-xe镜像通过环境变量自动创建用户和PDB,无需混用Testcontainers的用户配置方法,调整容器初始化代码:

ORACLE_OPIOWNER_CONTANINER = new OracleContainer("gvenzl/oracle-xe:21-slim")
        .withLogConsumer(new Slf4jLogConsumer(log))
        // 创建名为OPIOWNER的PDB
        .withEnv("ORACLE_DATABASE", "OPIOWNER")
        // 设置SYS/SYSTEM用户密码
        .withEnv("ORACLE_PASSWORD", "password")
        // 在OPIOWNER PDB中创建opiowner用户
        .withEnv("APP_USER", "opiowner")
        // 设置opiowner用户密码
        .withEnv("APP_USER_PASSWORD", "password")
        // 推荐用Service Name连接PDB,替换usingSid()
        .withServiceName("OPIOWNER");

ORACLE_OPIOWNER_CONTANINER.start();

// 直接使用容器提供的用户信息设置数据源
System.setProperty("spring.datasource.driver-class-name", ORACLE_OPIOWNER_CONTANINER.getDriverClassName());
System.setProperty("spring.datasource.url", ORACLE_OPIOWNER_CONTANINER.getJdbcUrl());
System.setProperty("spring.datasource.username", ORACLE_OPIOWNER_CONTANINER.getUsername());
System.setProperty("spring.datasource.password", ORACLE_OPIOWNER_CONTANINER.getPassword());

该配置会让镜像自动在指定PDB中创建opiowner用户,且Testcontainers会生成正确的JDBC URL指向该PDB。

方案2:手动执行SQL创建用户(兜底方案)

如果镜像环境变量不生效,可在容器启动后,用SYS用户手动创建并授权opiowner:

ORACLE_OPIOWNER_CONTANINER.start();

// 构建SYS用户的连接URL
String sysJdbcUrl = ORACLE_OPIOWNER_CONTANINER.getJdbcUrl().replace(ORACLE_OPIOWNER_CONTANINER.getUsername(), "SYS as SYSDBA");
try (Connection conn = DriverManager.getConnection(sysJdbcUrl, "SYS", "password")) {
    try (Statement stmt = conn.createStatement()) {
        // 创建用户
        stmt.execute("CREATE USER opiowner IDENTIFIED BY password");
        // 授予必要权限
        stmt.execute("GRANT CONNECT, RESOURCE, DBA TO opiowner");
        // 授予表空间无限配额
        stmt.execute("ALTER USER opiowner QUOTA UNLIMITED ON USERS");
    }
} catch (SQLException e) {
    log.error("创建opiowner用户失败", e);
    throw new RuntimeException(e);
}

// 后续设置数据源属性...

方案3:简化SQL脚本(避免指定schema前缀)

如果你的数据源连接用户就是opiowner,SQL脚本无需指定opiowner.前缀,默认会在当前用户的schema下创建表:

CREATE TABLE OPI_CONFLUENZA_DATI
(
    -- 字段定义不变...
);

ALTER TABLE OPI_CONFLUENZA_DATI
ADD CONSTRAINT OPI_CONFLUENZA_DATI_PK PRIMARY KEY ("OPI_TIPOLOGIADISPOSIZIONE", "OPI_DATADISPOSIZIONE", "OPI_ID_DISPOSIZIONE");

内容的提问来源于stack exchange,提问作者Saverio Mirko Viola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 06:07:02