在 Spring 中在 H2 数据库上集成测试 MySQL 语法的问题

Posted

技术标签:

【中文标题】在 Spring 中在 H2 数据库上集成测试 MySQL 语法的问题【英文标题】:Trouble with Integration Testing MySQL Syntax on an H2 Database in Spring 【发布时间】:2016-10-27 15:05:24 【问题描述】:

我正在开展一个项目,该项目使用不同的 mysql 模式进行客户管理,例如每个客户都有自己的数据库架构,它是使用骨架模板创建的。

出于兼容性问题,我必须从 Java 创建新的 MySQL 模式,这在 php 之前已经完成。我创建了一个小型服务类,它通过重复使用CREATE TABLE 'a' LIKE 'b'; 在 MySQL 数据库上完美运行,但是这不适用于 H2db,因为 H2 在创建表时不支持 LIKE-part。然后我创建了一个 MySQL 并修改了文件以便使用 Java 轻松处理(Files.readAllLines,它被输入到Statement.executeBatch)。但是,由于 COLLATION=UTF8 之类的语句,此转储在 H2db 上也会失败。遗憾的是,这些很重要,因为我们经常有需要正确编码的特殊字符,因此不建议简单地从 sql 文件中删除所述语句。

我这样初始化数据源:

public DataSource dataSource(ResourceLoader resourceLoader) 
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .setScriptEncoding("UTF-8")
            .addScript("data.sql").build();

所以,我的配置可能性很少。我们使用gradle 来构建我们的应用程序,所以建议的maven-plugin 也无济于事——至少我还没有发现如何将它与gradle 一起使用,如果可能的话。

所以我的问题是,我需要检查服务是否确实正确地保留了数据,但我不能在内存中进行,因为 H2 不支持语法,并且我不允许使用“真正的”MySQL 数据库,因为我们所有的测试都必须与“真正的”连接分离,并且在必要时只能使用内存中的数据库存储。

【问题讨论】:

【参考方案1】:

我最终使用https://github.com/vorburger/MariaDB4j 作为嵌入式数据库,因为无论如何我们都使用 MariaDB,所以这肯定会消除所有方言问题。由于这与嵌入式 MongoDB 的工作方式相同,因此额外的测试成本是可以接受的。

但需要注意的是:这需要 64 位操作系统才能工作。

【讨论】:

请指点这方面的信息?在我通过调用 dbService.start() 启动它之前,我的 MVC + JPA 测试一直尝试连接到数据库。 我不确定您需要什么样的指针 - 我使用 @ContextConfiguration 指向测试本身中的静态 TestConfiguration 类。在此我定义了一个DataSource-Bean,在初始化之前,它会从MariaDB4j 调用启动方法。【参考方案2】:

您使用 MySQL。 你有几种可能。

我使用的几个:

1.使用凹槽(插件)在gradle中创建您为mysql测试创建新数据库并以相同的方式创建架构。

gradle 中的一些配置可以帮助您。

def dbUser = 'root'
def dbPasswd = 'root'
def dbHost = 'localhost'
def dbName = 'testDB'
def dbPort = '3306'
def dbUrl = 'jdbc:mysql:////localhost:3306/testDB?
useUnicode=yes&characterEncoding=UTF-8'

dependencies  
        testCompile files('libs/junit-4.12.jar')
        testCompile files('libs/hamcrest-core-1.3.jar')
        testCompile files('libs/mysql-connector-java-5.1.29.jar')
       

//create DB for test
task loadDriver 
    URLClassLoader loader = GroovyObject.class.classLoader
    loader.addURL(file('libs/mysql-connector-java-5.1.29.jar').toURL())
    java.sql.DriverManager.registerDriver(loader.loadClass('com.mysql.jdbc.Driver').newInstance())


task createTestData(dependsOn: loadDriver) 

//  println 'Connecting to database ...'
//  def sql = groovy.sql.Sql.newInstance('jdbc:mysql://localhost:3306/?useUnicode=yes&characterEncoding=UTF-8', dbUser, dbPasswd, 'com.mysql.jdbc.Driver')
//  println '... connected'

//    println 'Drop test database testDB...'
//    sql.execute 'drop database IF exists testDB;'
//    println 'database testDB is droped'

//    println 'Create test database testDB...'
//    sql.execute 'create database if not exists testDB'
//    println 'Database testDB is created'


2.您可以在 java 中以类似的方式创建您在测试中使用的基类来创建 testDB mysql 例如:

public class TestDAO 
@Before
public void setUp() throws ClassNotFoundException, SQLException 

    java.util.Date date = new java.util.Date();
    LOG.info("Set up database for test "+date.getTime());
    database = database + date.getTime();
    LOG.info("DATABASE NAME:"+database);


    LOG.info("Create database:"+database);
    try 
        Class.forName(JDBC_DRIVER);
        conn = DriverManager.getConnection(URL+":"+PORT, USER, PASS);
        stmt = conn.createStatement();
        String sql = "CREATE DATABASE " + database;
        stmt.executeUpdate(sql);
     finally 
        if (stmt != null)
            stmt.close();
        if (conn != null)
            conn.close();
    

人们还可以发明其他一些方法。

【讨论】:

我认为这与我最终所做的最接近 - 我发现这个 (github.com/vorburger/MariaDB4j) 有效地创建了一个非常类似于嵌入式 MongoDB 的“嵌入式”MariaDB【参考方案3】:

数据库迁移工具

使用数据库迁移工具构建和重新构建您的数据库架构,例如:

Flyway Liquibase

不要直接编辑您的数据库,而是编写 SQL 脚本和/或 Java JDBC 类来定义您的数据库架构。创建表、定义列、导入数据、插入预期的行,所有这些都在 SQL 或 Java 中作为 SQL 脚本和/或 Java 类的集合。在测试时,这些工具中的任何一个都将通过执行您不断增长的脚本/类集合作为指令来创建和加载一个新的数据库。测试后,删除数据库。

为此目的,您的 Java 类可以酌情使用特定于 MySQL 或 H2 的替代语法。

在生产中使用 H2

此外,您可以完全跳过 MySQL,并在生产中使用 H2。

您可以通过为每个客户提供自己单独的 H2 数据库来解决您的multi-tenancy 问题,这意味着每个客户都有自己的物理数据文件。

上述数据库迁移工具通过在您的数据库中创建一个带有版本信息的额外表来工作。部署应用程序时,应用程序首先运行迁移工具以更新数据库结构和数据,并始终保持最新状态。这使得在生产中拥有许多单独的数据库变得很容易,每个数据库在下次使用时在生产中自动更新。

您的数据库服务器上需要足够的内存来支持尽可能多的多个数据库,这些数据库可能同时打开并由 H2 使用。对于大量租户,您可以运行多个数据库服务器,每个服务器都包含您租户数据库的一个子集。

有关详细信息,请参阅Multiple independent H2 databases within one JVM

【讨论】:

遗憾的是,在生产中使用 H2 实际上是不可能的,因为这是一个旧项目,并且有大量数据(和数据库)散布在 MariaDB 及其方言中。

以上是关于在 Spring 中在 H2 数据库上集成测试 MySQL 语法的问题的主要内容,如果未能解决你的问题,请参考以下文章

在内存数据库中使用 H2 进行 Spring Boot 集成测试

使用 h2 和 spring boot 进行测试

Spring H2 Test DB 在每次测试之前不会重置

如何在spring boot中创建H2+flyway测试数据库?

您可以在 Autoconfigured Spring Boot H2 测试数据库上设置兼容模式吗?

Spring Test Dbunit,H2数据库