jOOQ неправильно переводит SQL для базы данных H2 в режиме PostgreSQL - PullRequest
2 голосов
/ 10 января 2020

Проблема с синтаксисом Postgres ON CONFLICT.

Версии (зависимости maven):

  • postgresql: 42.2.9
  • jooq : 3.12.3
  • h2database: 1.4.200
// mocking connection
final Connection connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;Mode=PostgreSQL", "sa", "");
final Settings settings = new Settings().withRenderNameStyle(RenderNameStyle.AS_IS);
Mockito.doReturn(DSL.using(connection, SQLDialect.POSTGRES, settings)).when(mockDbConn).getDSLContext();

// java code for upsert:
dc.insertInto(MY_TABLE)
    .columns(MY_TABLE.TOKEN, MY_TABLE.NAME, MY_TABLE.EMAIL)
    .values(token, name, email)
    .onDuplicateKeyUpdate()
    .set(MY_TABLE.EMAIL, email)
    .execute();

, получая следующий журнал ошибок (кажется, что проблема может быть из-за [*] (строка 4 ↓). I не могу понять, почему он появляется и как его удалить):

-- Syntax error in SQL statement:
INSERT INTO PUBLIC.MY_TABLE (TOKEN, NAME, EMAIL)
VALUES (?, ?, ?)
ON CONFLICT ([*]TOKEN, NAME) -- line 4
DO UPDATE SET EMAIL = EXCLUDED.EMAIL;

-- expected "DO";
-- SQL statement:
insert into public.my_table (token, name, email)
values (?, ?, ?)
on conflict (token, name)
do update set email = excluded.email;

-- [42001-200]

Вот что происходит, если я переключаю диалект с SQLDialect.POSTGRES на SQLDialect.H2:

-- Column "EXCLUDED.EMAIL" not found; SQL statement:
merge into public.my_table using (select 1 one)
on (public.my_table.token = cast(? as varchar) and public.my_table.name = cast(? as varchar))
when matched then update set public.my_table.email = excluded.email
when not matched then insert (token, name, email)
values (cast(? as varchar), cast(? as varchar), cast(? as varchar))

-- [42122-200]

1 Ответ

1 голос
/ 13 января 2020

Вы используете три диалекта в своем использовании jOOQ API:

  1. Диалект SQLDialect.MYSQL, который является диалектом, который произвел синтаксис onDuplicateKeyUpdate(). Это можно эмулировать на разных диалектах, но обычно лучше использовать собственный синтаксис (SQL стандартный MERGE, если доступен, или ON CONFLICT в PostgreSQL).
  2. * SQLDialect.POSTGRES диалект, который является вашим целевым производственным диалектом
  3. диалект SQLDialect.H2, который вы используете для тестирования интеграции

Это большая сложность, учитывая тот факт, что вы вероятно, предназначается только для PostgreSQL в качестве продукта производственной базы данных. Я настоятельно рекомендую вам использовать testcontainers для интеграционного тестирования, что позволит вам удалить H2 из уравнения. Кроме того, если вы выбрали PostgreSQL только в качестве целевого диалекта, вы можете избежать синтаксиса onDuplicateKeyUpdate() и использовать встроенную поддержку синтаксиса onConflict() в jOOQ для более предсказуемых результатов.

Если вы продолжите смешивая 3 вышеупомянутых диалекта, вы часто сталкиваетесь с некоторыми ограничениями, когда jOOQ или H2 не могут эмулировать используемый вами синтаксис. Это только приемлемая ситуация, если вам действительно нужно поддерживать 3 диалекта в производстве.

...