Поддержка MyBatis для нескольких баз данных - PullRequest
0 голосов
/ 02 мая 2019

У меня есть разные клиенты, которые используют разных поставщиков баз данных (postgres, oracle, mysql и т. Д.)

Я хотел бы написать свой код один раз и иметь возможность работать с разными базами данных.

Каков был бы «mybatis» способ достижения этого?

Проблемы, которые я обнаружил, например:

  • У postgres есть понятие «если не существует» при созданииSQL заявления.oracle не поддерживает это.
  • oracle не имеет поддержки "limit" и "offset" в синтаксисе sql, в то время как другие db поддерживают.
  • text (postgres) vs clob (oracle и другие)jdbc печатает в выражениях DDL.

Я не хочу дублировать мои запросы (это то, что я делал до сих пор).Возможно, есть более элегантный способ сделать это.

Я использую mybatis java annotaions.

1 Ответ

1 голос
/ 02 мая 2019

Вы говорите, что используете аннотации, но я бы предложил использовать для этого XML. Я обычно нахожу это более понятным, особенно потому, что есть части запроса, которые вам все равно придется полностью изменить.

Вот пример DDL, где вы получаете два отдельных элемента XML для одного и того же метода, но с разными идентификаторами базы данных. Эти таблицы очень похожи, но из-за того, как вы проверяете, существует ли таблица и типы ли существенно различаются, вы не можете избежать отдельного кода SQL для этого:

<update id="createTables" databaseId="postgresql">
    DO $$
    BEGIN
        CREATE TABLE IF NOT EXISTS item (
            id SERIAL PRIMARY KEY,
            content TEXT,
            creation_datetime TIMESTAMPTZ DEFAULT NOW(),
            modification_datetime TIMESTAMPTZ
        );
    END$$
</update>
<update id="createTables" databaseId="sqlserver">
    IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'item')
    BEGIN
        CREATE TABLE item (
            id INT IDENTITY PRIMARY KEY,
            content NVARCHAR(MAX),
            creation_datetime DATETIMEOFFSET DEFAULT SYSDATETIMEOFFSET(),
            modification_datetime DATETIMEOFFSET
        );
    END
</update>

Вы также можете иметь запрос в элементе SQL, но различные разделы могут быть изменены с помощью <if> (или <choose>) в зависимости от идентификатора базы данных. Это работает нормально, если изменения невелики:

<delete id="deleteItem">
    DELETE FROM my_item
    WHERE gid=CAST(#{gid} AS <if test="_databaseId == 'postgresql'">UUID</if><if test="_databaseId == 'sqlserver'">UNIQUEIDENTIFIER</if>)
</delete>

<select id="getLatestSomething" resultType="test.Something">
    SELECT <if test="_databaseId == 'sqlserver'">TOP 1</if> *
    FROM something
    ORDER BY creation_datetime DESC
    <if test="_databaseId == 'postgresql'">
    LIMIT 1
    </if>
</select>

Выбор между наличием отдельных элементов запроса для разных идентификаторов базы данных или просто условных фрагментов в одном и том же элементе запроса зависит от удобства чтения. Это может быть весьма субъективно в зависимости от сложности запроса.

Например, я нахожу следующую "упущенную" с использованием PostgreSQL и SQL Server довольно трудной для чтения. Было бы лучше в отдельных элементах:

<insert id="insertStuff" parameterType="somestuff.Stuff">
    <if test="_databaseId == 'postgresql'">
        INSERT INTO my_stuff (...)
    </if>
    <if test="_databaseId == 'sqlserver'">
        MERGE INTO my_stuff WITH (HOLDLOCK) AS t USING (
    </if>
            VALUES (#{...},
                    <if test="_databaseId == 'postgresql'">
                        CAST(#{jsonData} AS JSONB)
                    </if>
                    <if test="_databaseId == 'sqlserver'">
                        #{jsonData}
                    </if>
            )
    <if test="_databaseId == 'postgresql'">
            ON CONFLICT DO NOTHING
    </if>
    <if test="_databaseId == 'sqlserver'">
        )
              AS s (...)
              ON t....=s....
                 AND t....=s....
        WHEN NOT MATCHED BY TARGET THEN
            INSERT (...)
            VALUES (s...., s....);
    </if>
</insert>

Более подробная информация об этом содержится в документации MyBatis Dynamic SQL .


Если ваш файл сопоставления XML находится в mypackage/MyMapper.xml, вы можете использовать интерфейс Java MyMapper в пакете, соответствующем этому каталогу. Там нет ничего конкретного для идентификатора базы данных.

package mypackage;

public interface MyMapper {
    void createTables();
    void deleteItem(@Param("gid") UUID gid);
    Something getLatestSomething();
}

При использовании MyBatis с Spring вы можете установить конфигурацию поставщика следующим образом:

@Bean
public VendorDatabaseIdProvider vendorDatabaseIdProvider() {
    Properties vendorProperties = new Properties();
    vendorProperties.setProperty("PostgreSQL", "postgresql");
    vendorProperties.setProperty("SQL Server", "sqlserver");
    // Add others as required, this will look for the substring in the product name coming
    // from the database metadata.

    // ...
    VendorDatabaseIdProvider dbIdProvider = new VendorDatabaseIdProvider();
    dbIdProvider.setProperties(vendorProperties);
    return dbIdProvider;
}

@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource, ApplicationContext appContext,
        VendorDatabaseIdProvider vendorDatabaseIdProvider) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    bean.setDatabaseIdProvider(vendorDatabaseIdProvider);

    SqlSessionFactory factory = bean.getObject();
    return factory;
}

Если вы не используете Spring, вы сможете настроить DatabaseIdProvider с помощью XML-конфигурации .

Вам строго не нужен поставщик идентификатора базы данных. Все, что он делает, это устанавливает идентификатор базы данных в конфигурации, основываясь на имени продукта, которое он получает из DataSource. Что-то в этом роде:

String databaseId = databaseIdProvider.getDatabaseId(dataSource);
configuration.setDatabaseId(databaseId);

(Если вы посмотрите на код org.apache.ibatis.mapping.VendorDatabaseIdProvider, вы увидите, что databaseIdProvider.getDatabaseId(...) просто ищет подходящую подстроку в том, что возвращается из DatabaseMetaData.getDatabaseProductName(). Вы также можете сделать это вручную с помощью других настроек, если это необходимо. )

Обратите внимание, что когда databaseId="" используется непосредственно в качестве атрибута элемента XML, подчеркивание отсутствует, но когда оно используется в качестве условия проверки, оно называется _databaseId.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...