如何在Oracle Testcontainer中结合Liquibase与Bootique使用多Schema并解决重复标识符问题
我来帮你梳理下解决这个问题的具体步骤,分两部分搞定:先创建多Schema,再隔离Liquibase的变更记录避免ID冲突。
第一步:在Oracle Testcontainer中创建额外的Schema
Oracle Testcontainer确实没有直接创建多Schema的API,但我们可以在容器启动后,通过JDBC连接手动执行SQL来创建新的Schema(Oracle里Schema和用户是绑定的,创建用户就等于创建了对应的Schema),还得给这个新用户授予必要的权限才能正常使用。
举个代码例子:
// 先定义Oracle容器 static final OracleContainer oracleContainer = new OracleContainer("gvenzl/oracle-xe:21-slim-faststart"); static { oracleContainer.start(); // 创建第二个Schema(用户)并授权 try (Connection conn = DriverManager.getConnection( oracleContainer.getJdbcUrl(), oracleContainer.getUsername(), oracleContainer.getPassword())) { try (Statement stmt = conn.createStatement()) { // 创建SCHEMA2用户,密码设为SCHEMA2PASS stmt.execute("CREATE USER SCHEMA2 IDENTIFIED BY SCHEMA2PASS"); // 授予连接、资源管理等必要权限 stmt.execute("GRANT CONNECT, RESOURCE, DBA TO SCHEMA2"); } } catch (SQLException e) { throw new RuntimeException("创建第二个Schema失败", e); } }
第二步:为每个Schema独立配置Bootique DbTester与Liquibase
你之前的问题是用同一个DbTester连续调用两次initDBWithLiquibaseChangelog,这样两个变更日志都会被应用到同一个Schema(USER),而且如果两个日志里有相同的changeSet ID,Liquibase就会报重复标识符错误——因为它默认用同一个DATABASECHANGELOG表来记录变更。
解决办法很直接:给每个Schema单独创建一个DbTester实例,让它们分别连接到对应的Schema,各自应用自己的变更日志。这样每个Schema的Liquibase变更记录会存在自己Schema下的DATABASECHANGELOG表里,完全隔离,自然不会有ID冲突。
修改你的测试代码如下:
@BQTestTool(BQTestScope.GLOBAL) static final DbTester<TcDbTester> schema1Tester = TcDbTester .db(oracleContainer, "USER", "USERPASS") .initDBWithLiquibaseChangelog("classpath:changelogSchema1.yml"); @BQTestTool(BQTestScope.GLOBAL) static final DbTester<TcDbTester> schema2Tester = TcDbTester .db(oracleContainer, "SCHEMA2", "SCHEMA2PASS") .initDBWithLiquibaseChangelog("classpath:changelogSchema2.yml");
这样一来,changelogSchema1.yml会被应用到USER Schema,changelogSchema2.yml会被应用到SCHEMA2 Schema,两者的变更记录互不干扰,哪怕两个日志里有相同的changeSet ID也没关系。
额外提示
如果你的变更日志里没有明确指定Schema,记得在每个changeSet的数据库对象前加上对应的Schema前缀,或者在Liquibase配置里设置默认Schema,确保对象创建到正确的Schema下。比如在changelogSchema2.yml里,所有表、视图等对象都加上schemaName: SCHEMA2的配置。
备注:内容来源于stack exchange,提问作者dschulten

