尽管加载了正确的应用程序上下文,JUnit 仍测试访问错误的数据库

JUnit tests accessing the wrong database despite loading the correct application context

我目前无法解释我在这里看到的内容 - 如果您需要更多信息,请告诉我

我有一个应用程序上下文文件

src/test/resources/applicationContext-jooq-test.xml

看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing 
        infrastructure -->

    <!-- This is needed for the @Transactional annotation -->
    <tx:annotation-driven transaction-manager="transactionManager" />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the ${webappRoot}/resources directory -->
    <mvc:resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources 
        in the /WEB-INF/views directory -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <context:component-scan base-package="com.mz.server" />

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/mz_testdb?useSSL=false" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <!-- Configure Spring's transaction manager to use a DataSource -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- Configure jOOQ's TransactionProvider as a proxy to Spring's transaction manager -->
    <bean id="transactionProvider"
        class="com.mz.server.SpringTransactionProvider">
    </bean>

    <!-- Configure jOOQ's ConnectionProvider to use Spring's TransactionAwareDataSourceProxy,
         which can dynamically discover the transaction context -->
    <bean id="transactionAwareDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
        <constructor-arg ref="dataSource" />
    </bean>

    <bean class="org.jooq.impl.DataSourceConnectionProvider" name="connectionProvider">
        <constructor-arg ref="transactionAwareDataSource" />
    </bean>

    <!-- Configure the DSL object, optionally overriding jOOQ Exceptions with Spring Exceptions -->
    <bean id="dslContext" class="org.jooq.impl.DefaultDSLContext">
        <constructor-arg ref="config" />
    </bean>

    <!-- Invoking an internal, package-private constructor for the example
         Implement your own Configuration for more reliable behaviour -->
    <bean class="org.jooq.impl.DefaultConfiguration" name="config">
        <property name="SQLDialect"><value type="org.jooq.SQLDialect">MYSQL</value></property>
        <property name="connectionProvider" ref="connectionProvider" />
        <property name="transactionProvider" ref="transactionProvider" />
    </bean>

</beans>

如您所见,我清楚地mz_testdb定义为我的目标数据库。

对于我的 JUnit 测试,我有一个 AbstractRepositoryTest 除了初始化数据库并在测试开始之前将其置于有用状态之外什么都不做:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/applicationContext-jooq-test.xml")
public abstract class AbstractRepositoryTest {

    private final static Logger LOGGER = Logger.getLogger(AbstractRepositoryTest.class.getName());

    private static DSLContext ctx;

    public AbstractRepositoryTest() {

    }

    @BeforeClass
    public static void setUpDatabase() {

        LOGGER.debug("Setting up database for unit tests ..");

        @SuppressWarnings("resource")
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-jooq-test.xml");
        DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");

        FlywayDbMain flywayDbMain = new FlywayDbMain();

        boolean databaseOkay = flywayDbMain.setUpCleanDatabase(dataSource);

        AbstractRepositoryTest.ctx = (DSLContext) applicationContext.getBean("dslContext");

        org.junit.Assume.assumeTrue(databaseOkay);

    }

    protected static DSLContext getContext() {
        return AbstractRepositoryTest.ctx;
    }
}

当然我正在准备考试:

public class ShopRepositoryTest extends AbstractRepositoryTest {

    private final static Logger LOGGER = Logger.getLogger(ShopRepositoryTest.class.getName());

    private ShopRepository shopRepository;

    private Long shop1Id;

    private Long shop2Id;

    private final static Long LANG_GERMAN = 1L;

    private final static Long LANG_ENGLISH = 2L;

    @Before
    public void before() {

        try {

            this.shopRepository = new ShopRepository(AbstractRepositoryTest.getContext());

            LOGGER.info("Setting up database ..");

            // Shop 1

            this.shop1Id = this.shopRepository.createShop("Shop 1", 46.061785f, 16.464123f, "Europe/Vienna", LANG_GERMAN);

            this.shopRepository.addSupportedLanguage(this.shop1Id, LANG_GERMAN);
            this.shopRepository.addSupportedLanguage(this.shop1Id, LANG_ENGLISH);

            // Shop 2

            this.shop2Id = this.shopRepository.createShop("Shop 2", 46.061785f, 16.464123f, "Europe/Vienna", LANG_GERMAN);

            this.shopRepository.addSupportedLanguage(this.shop2Id, LANG_GERMAN);
            this.shopRepository.addSupportedLanguage(this.shop2Id, LANG_ENGLISH);

        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void deleteAllItemDetails() {
        // ...
    }

    @After
    public void after() {
        LOGGER.debug("All done.");
    }

}

现在,我得到的错误如下:

Caused by: java.sql.SQLSyntaxErrorException: Table 'mz_db.shop' doesn't exist
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:686)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:663)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:653)

我已经从我的代码中调试了飞船,但我就是看不到它。 哪里为什么我突然与我的开发数据库mz_db对话?如果我遍历所有代码,我会看到 url 实际上是正确的:

其实有说mz_db,不过

也就这些了
/src/main/webapp/WEB-INF/spring/applicationContext-jooq.xml

当然,如果我创建 mz_db,我不会收到此异常,但所有数据都会写入此 table,这不是我想要的。

我很好奇我是怎么做到这样的事情的,这一定是我没有看到的愚蠢行为。

这里发生了什么?


我已经试过了

jOOQ 本身并不关心您的 JDBC 连接 URL。这只是当 table 不完全合格时使用的默认数据库。但是 jOOQ 默认情况下完全限定 tables 模式名称(= MySQL 中的数据库名称)。因此,如果您从 mz_db 数据库生成 jOOQ tables 并针对 mz_testdb 连接进行 运行 查询,生成的 SQL 仍将完全符合 mz_db.shop table 与最初生成的一样。

有两种方法可以解决这个问题:

  1. 使用 Settings.renderSchema
  2. 禁用完全符合条件的 tables
  3. 使用 Settings.renderMapping 配置架构映射以将 mz_db 重写为 mz_testdb

后者也记录在这里: http://www.jooq.org/doc/latest/manual/sql-building/dsl-context/runtime-schema-mapping