繁体   English   中英

Arquillian 和 Flyway 使用不同版本的 H2 内存

[英]Arquillian and Flyway using different versions of H2 in-memory

我正在尝试创建一个 JPA-helper,它最终将变成 jar 并用于其他项目。 我想向这个项目添加集成测试,所以我添加了一个TestEntityTestService class (这些是指DB模式中的一个简单的TEST表)。

目标是使用H2创建内存数据库,使用Arquillian在容器中运行测试,并使用Flyway创建/填充测试数据库。 Flyway 由@ApplicationScoped class 的侦听器触发。

但是,每当我触发集成测试时,测试似乎都可以正常工作,直到调用count(*)查询。 此时,Hibernate 抱怨架构(刚刚在 Flyway 中创建)不存在: Schema 'DB' does not exist

有很多日志,但亮点看起来是这样的:

  • 启动了一个测试 Glassfish 服务器
  • Hibernate 启动
    • 它还没有抱怨任何事情
    • 如果hibernate.hbm2ddl.auto设置为validate ,则它抱怨在 Flyway 中创建的表不存在( Schema-validation: missing table [DB.TEST]
  • Flyway 执行数据迁移
    • 它说两个脚本都成功执行
  • 测试代码开始运行
  • 代码失败
    • Schema 'DB' does not exist

我认为我需要做的

我认为 Flyway 和 EntityManager 正在与数据库的不同实例通信。 Flyway 所做的更改不会以 EntityManager 可以看到的方式保存。

  • 确保EntityManagerFlyway正在与同一个数据库通信。
    • 名称相同(请参阅persistence.xmlEntityManagerFactoryFlywayDataLoader ),我尝试添加/删除各种MODE值但没有任何效果。
  • 强制 Flyway 代码在 Hibernate 代码之前执行
  • 在测试开始之前强制 Hibernate 刷新它的架构

我试过的

  • 由于 SQL 错误,将语法错误放入 Flyway 迁移代码会导致测试失败。
    • 如果您将重复语句放入 SQL 代码中(例如:再次创建表或模式),也会发生同样的情况。
  • 我尝试通过 JDBC 连接字符串中的启动脚本触发 Flyway
    • 即: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:create.sql'
    • 我知道脚本触发了(因为添加错误会导致测试失败),但它仍然没有解决Schema 'DB' does not exist问题。
  • 各种口味的hibernate.hbm2ddl.auto
    • none : Schema 'DB' does not exist
    • validateSchema-validation: missing table [DB.TEST]
    • update , create-drop :表已创建,但 Flyway 中的数据丢失
  • 将 Flyway 代码放入EntityManagerFactory.create()
  • 将 Flyway 代码放入积分器 class参考
  • 更改 Flyway/persistence.xml 连接以使用 DB 文件而不是 memory(例如: jdbc:h2:~/test-db
    • 这会在C:\Users\<me>中创建一个名为test-db.mv的文件。 该文件显示 Flyway 与其交互的证据,但 EntityManager 仍然生成Schema 'DB' does not exist错误。
    • 我还将hibernate.hbm2ddl.auto设置为create ,然后在我的整个硬盘驱动器中搜索第二个test-db.mv实例,但找不到。

什么行不通

  • Arquillian 坚持
    • 看起来很棒,但我在公司网络上,他们还没有批准
  • 春天-*
    • 这个项目不使用 Spring 任何东西

日志和代码

执行日志

// various "server startup" logs
// ...
// misc hibernate logs
Apr 15, 2020 9:40:39 AM org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
    name: TestDS
    ...]
// ...
Loading Flyway Data
8 [main] INFO org.flywaydb.core.internal.util.VersionPrinter - Flyway Community Edition 5.0.7 by Boxfuse
246 [main] INFO org.flywaydb.core.internal.database.DatabaseFactory - Database: jdbc:h2:mem:test (H2 1.4)
354 [main] INFO org.flywaydb.core.internal.command.DbValidate - Successfully validated 2 migrations (execution time 00:00.028s)
377 [main] INFO org.flywaydb.core.internal.schemahistory.JdbcTableSchemaHistory - Creating Schema History table: "PUBLIC"."flyway_schema_history"
413 [main] INFO org.flywaydb.core.internal.command.DbMigrate - Current version of schema "PUBLIC": << Empty Schema >>
415 [main] INFO org.flywaydb.core.internal.command.DbMigrate - Migrating schema "PUBLIC" to version 1 - CreateDatabase
436 [main] INFO org.flywaydb.core.internal.command.DbMigrate - Migrating schema "PUBLIC" to version 2 - AddTestClasses
449 [main] INFO org.flywaydb.core.internal.command.DbMigrate - Successfully applied 2 migrations to schema "PUBLIC" (execution time 00:00.078s)
Apr 15, 2020 9:40:44 AM com.sun.enterprise.web.WebApplication start
INFO: Loading application [test] at [/test]
Apr 15, 2020 9:40:45 AM org.glassfish.deployment.admin.DeployCommand execute
INFO: test was successfully deployed in 7,649 milliseconds.
Apr 15, 2020 9:40:45 AM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: 
    select
        count(testobject0_.id) as col_0_0_ 
    from
        DB.TEST testobject0_ 
    where
        1=1
Apr 15, 2020 9:40:45 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 30000, SQLState: 42Y07
Apr 15, 2020 9:40:45 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Schema 'DB' does not exist

// stack trace
/ ...

集成测试代码

@RunWith(Arquillian.class)
public class ProviderTest {

    @Deployment
    public static JavaArchive createDeployment() {
        JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
                .addClasses(
                        TestObject.class,
                        TestService.class,
                        QueryValuesProviderImpl.class,
                        CriteriaQueryProviderFactory.class,
                        EntityManagerFactory.class
                        )
                .addClass(FlywayDataLoader.class)
                .addAsResource("META-INF/persistence.xml")
                .addAsManifestResource(new ByteArrayAsset(new byte[0]), ArchivePaths.create("beans.xml"));
        System.out.println(jar.toString(true));
        return jar;
    }

    @Inject
    private TestService testService;

    @Test
    public void testGetTestResource() {
        List<TestObject> data = testService.getTestObjects(new QueryValues());

        assertNotNull(data);
        assertEquals(2, data.size());
    }
}

飞路 Java 脚本

@ApplicationScoped
public class FlywayDataLoader {

    private static final String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
    private static boolean initialized = false;

    public static void setup(
            @Observes @Initialized(ApplicationScoped.class) final Object event
    ) {
        if (!initialized) {
            System.out.println("Loading Flyway Data");
            Flyway flyway = new Flyway();
            flyway.setDataSource(JDBC_URL, "sa", "");
            flyway.setBaselineOnMigrate(true);
            flyway.migrate();

            initialized = true;
        }
    }
}

Flyway SQL 脚本

这分为两个文件(一个用于创建模式/表,第二个用于插入实际数据)。 这些文件位于/resources/db.migration/文件夹中,分别命名为V1__CreateDatabase.sqlV2__AddTestClasses.sql

CREATE SCHEMA DB;

create table DB.TEST
(
    ID INT auto_increment
        constraint PK_REQUEST_AUDIT
            primary key,
    FN VARCHAR2(256 char) default NULL not null,
    LN VARCHAR2(256 char) default NULL not null,
    BD TIMESTAMP default SYSDATE not null
)
;

insert into DB.TEST (FN, LN, BD) VALUES ('Alice', 'Zyl', PARSEDATETIME('1985-03-13','yyyy-MM-dd','en'));
insert into DB.TEST (FN, LN, BD) VALUES ('Bart', 'Young', PARSEDATETIME('1988-03-25','yyyy-MM-dd','en'));

实体管理器

此处使用的unitName与 persistence.xml 中使用的persistence.xml Unit Name 匹配。 那里使用的 JDBC 连接字符串与 Flyway Java 脚本中使用的连接字符串相同。

public class EntityManagerFactory {

    @PersistenceUnit(unitName="TestDS")
    private javax.persistence.EntityManagerFactory emFactory;

    @Produces
    @Default
    @RequestScoped
    public EntityManager create() {
        return emFactory.createEntityManager();
    }

    public void dispose(@Disposes @Default EntityManager em){
        if(em.isOpen()){
            em.close();
        }
    }

}

持久性.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="TestDS" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>myproject.beans.TestObject</class>

        <properties>
            <!-- Configuring JDBC properties -->
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <!-- Hibernate properties -->
            <property name="hibernate.ddl-auto" value="none" />
            <property name="hibernate.connection.user" value="sa" />
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

不起眼的DAO class?

@RequestScoped
public class TestService {

    @Inject
    private EntityManager em;

    @Inject
    private CriteriaQueryProviderFactory criteriaQueryProviderFactory;

    public List<TestObject> getTestObjects(QueryValues queryValues) {
        CriteriaQueryProvider<TestObject> criteriaQueryProvider = criteriaQueryProviderFactory.getFactory();

        CriteriaQuery<TestObject> criteriaQuery = criteriaQueryProvider.buildModifiedQuery(queryValues, TestObject.class);
        TypedQuery<TestObject> typedQuery = criteriaQueryProvider.addSecondaryModifiers(criteriaQuery, queryValues, TestObject.class);

        TypedQuery<Long> countQuery = criteriaQueryProvider.buildCountQuery(queryValues, TestObject.class);

        System.out.println(String.format("There are %d objects total!", countQuery.getSingleResult()));

        return typedQuery.getResultList();
    }
}

the best way I found to integrate Flyway with Hibernate is to use an implementation of org.hibernate.integrator.spi.Integrator : https://docs.jboss.org/hibernate/orm/5.4/javadocs/org/hibernate/integrator/ spi/Integrator.html 该接口有两个方法, integratedisintegrate 如果您将运行 Flyway 的代码放入实现的integrate方法中,一切都应该可以正常工作。 Hibernate loads implementations of org.hibernate.integrator.spi.Integrator the service loader mechanism from the Java Standard API, therefore you have to create a file META-INF/services/org.hibernate.integrator.spi.Integrator which contains the fully qualified class 您的实现名称。

最好的

延斯

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM