Arquillian и Flyway используют разные версии H2 в памяти - PullRequest
0 голосов
/ 15 апреля 2020

Я пытаюсь создать JPA-помощник, который в конечном итоге будет превращен в банку и использован в других проектах. Я хочу добавить интеграционные тесты в этот проект, поэтому я добавил классы TestEntity и TestService (они относятся к простой таблице TEST в схеме DB).

Цель состоит в том, чтобы создать базу данных в памяти с H2 , запустить тест в контейнере с Arquillian и создать / заполнить тестовую базу данных с помощью * пролетный путь 1012 *. Flyway запускается при наличии прослушивателя для класса @ApplicationScoped.

Однако всякий раз, когда я запускаю интеграционный тест, тест, кажется, работает правильно, пока не будет вызван запрос count(*). На данный момент Hibernate жалуется, что схема (которая только что была создана в Flyway) не существует: Schema 'DB' does not exist

Есть много журналов, но основные моменты выглядят так:

  • Тестовый сервер Glassfi sh запущен
  • Запускается Hibernate
    • Он пока ни на что не жалуется
    • , если hibernate.hbm2ddl.auto установлен на validate затем он жалуется на то, что таблицы, созданные в Flyway, не существуют (Schema-validation: missing table [DB.TEST])
  • Flyway выполняет миграцию данных
    • сообщает, что оба сценария выполнены успешно
  • Тестовый код запускается
  • Код не выполняется
    • Schema 'DB' does not exist

Что я думаю, мне нужно сделать

Я думаю, что Flyway и EntityManager общаются с разными экземплярами базы данных. Изменения, которые выполняет Flyway, не сохраняются таким образом, чтобы EntityManager мог их видеть.

  • Убедитесь, что EntityManager и Flyway общаются с одной и той же базой данных.
    • Имя идентично (см. persistence.xml, EntityManagerFactory и FlywayDataLoader), и я попытался добавить / удалить различные значения MODE без эффекта.
  • Принудительно выполнить код Flyway перед кодом Hibernate
  • Принудительно обновить Hibernate sh это схема перед началом тестирования

Что я пробовал

  • Внесение синтаксической ошибки в код миграции Flyway приводит к сбою теста из-за ошибки SQL.
    • То же самое происходит, если вы вставляете дублирующий оператор в код SQL (например, создаете таблицу или схему во второй раз).
  • Я попытался запустить Flyway через сценарий запуска в строке подключения JDB C
    • т.е.: 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
    • validate: Schema-validation: missing table [DB.TEST]
    • update, create-drop: таблицы созданы, но данные с Flyway отсутствуют
  • Введите код Flyway в пределах EntityManagerFactory.create()
  • Поместите код Flyway в Класс интегратора и , ссылка
  • Измените соединение Flyway / Persistence. xml, чтобы использовать файл для БД а не память (например: jdbc:h2:~/test-db).
    • Это создает файл в C:\Users\<me> с именем test-db.mv. Файл показывает, что Flyway взаимодействует с ним, но EntityManager по-прежнему генерирует ошибку Schema 'DB' does not exist.
    • Я также установил hibernate.hbm2ddl.auto на create, затем произвел поиск всего моего жесткого диска для второго экземпляра test-db.mv, но не смог найти.

Что не сработает

  • Стойкость Арквиллия
    • Это выглядит потрясающе, но я в сети компании, и они еще не одобрили его
  • Spring- *
    • Этот проект не использует 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());
    }
}

Flyway Java Script

@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 script

Это разделено на два файла (один для создания схемы / таблиц, второй для вставки фактических данных). Файлы находятся в папке /resources/db.migration/ и имеют имена V1__CreateDatabase.sql и V2__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'));

EntityManager

Используемое здесь unitName соответствует имени единицы сохраняемости, используемому в persistence.xml. Используемая там строка соединения JDB C идентична той, которая используется в сценарии 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();
        }
    }

}

Persistence. 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>

Unremarkable 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();
    }
}

1 Ответ

1 голос
/ 15 апреля 2020

лучший способ интеграции Flyway с Hibernate, который я нашел, - это использовать реализацию org.hibernate.integrator.spi.Integrator: https://docs.jboss.org/hibernate/orm/5.4/javadocs/org/hibernate/integrator/spi/Integrator.html. Этот интерфейс имеет два метода, integrate и disintegrate. Если вы поместите код для запуска Flyway в метод integrate вашей реализации, все должно работать нормально. Hibernate загружает реализации org.hibernate.integrator.spi.Integrator механизма загрузчика служб из Java Standard API, поэтому вам нужно создать файл META-INF/services/org.hibernate.integrator.spi.Integrator, который содержит полное имя класса вашей реализации.

Best

Jens

...