Вступление
jOOQ — это отличный фреймворк, когда вы хотите работать с SQL в Java без чрезмерного использования ORM. В то же время его можно интегрировать во многие среды, поскольку он предлагает вам поддержку многих специфичных для базы данных функций. Одной из таких специфичных для базы данных функций является разбиение в PostgreSQL. Разбиение в PostgreSQL в основном используется из соображений производительности, поскольку в определенных ситуациях это может повысить производительность запросов. У jOOQ нет явной поддержки этой функции, но ее можно легко интегрировать, как мы вам покажем.
Эта статья предоставлена вам немецким партнером по интеграции JOOQ UWS Software Service (UWS) . UWS специализируется на разработке программного обеспечения, модернизации приложений и аутсорсинге, уделяя особое внимание экосистеме Java Enterprise.
Разбиение в PostgreSQL
Благодаря функции разделения PostgreSQL у вас есть возможность разбить данные, которые бы образовали огромную таблицу, на несколько отдельных таблиц. Каждый из разделов является нормальной таблицей, которая наследует свои столбцы и ограничения от родительской таблицы. Это так называемое наследование таблиц может использоваться для «разделения диапазона», когда, например, данные из одного диапазона не перекрывают данные из другого диапазона с точки зрения идентификаторов, дат или других критериев.
Как и в следующем примере, у вас может быть разбиение для таблицы «author», которая использует один и тот же внешний ключ таблицы «authorgroup» во всех ее строках.
CREATE TABLE author ( authorgroup_id int, LastName varchar(255) ); CREATE TABLE author_1 ( CONSTRAINT authorgroup_id_check_1 CHECK ((authorgroup_id = 1)) ) INHERITS (author); CREATE TABLE author_2 ( CONSTRAINT authorgroup_id_check_2 CHECK ((authorgroup_id = 2)) ) INHERITS (author); ...
Как вы можете видеть, мы настраиваем наследование и — чтобы иметь простой пример — мы просто помещаем одно ограничение, проверяющее, что у разделов одинаковый «authorgroup_id». В основном это приводит к тому, что таблица «author» содержит только определения таблиц и столбцов, но не содержит данных. Однако при запросе к таблице «author» PostgreSQL действительно запрашивает все наследующие таблицы «author_n», возвращая объединенный результат.
Тривиальный подход к использованию jOOQ с разметкой
Для работы с разделами, описанными выше, jOOQ предлагает несколько вариантов. Вы можете использовать способ по умолчанию, который позволяет jOOQ генерировать один класс на таблицу. Чтобы вставить данные в несколько таблиц, вам придется использовать разные классы. Этот подход используется в следующем фрагменте:
// add InsertQuery query1 = dsl.insertQuery(AUTHOR_1); query1.addValue(AUTHOR_1.ID, 1); query1.addValue(AUTHOR_1.LAST_NAME, "Nowak"); query1.execute(); InsertQuery query2 = dsl.insertQuery(AUTHOR_2); query2.addValue(AUTHOR_2.ID, 1); query2.addValue(AUTHOR_2.LAST_NAME, "Nowak"); query2.execute(); // select Assert.assertTrue(dsl .selectFrom(AUTHOR_1) .where(AUTHOR_1.LAST_NAME.eq("Nowak")) .fetch().size() == 1); Assert.assertTrue(dsl .selectFrom(AUTHOR_2) .where(AUTHOR_2.LAST_NAME.eq("Nowak")) .fetch().size() == 1);
Вы можете видеть, что нужно использовать несколько классов, сгенерированных jOOQ, поэтому в зависимости от того, сколько у вас разделов, сгенерированные классы могут загрязнять вашу кодовую базу. Кроме того, представьте, что вам в конечном итоге потребуется перебирать разделы, что было бы обременительно при таком подходе. Другой подход может состоять в том, что вы используете jOOQ для построения полей и таблиц с использованием строковых манипуляций, но это снова подвержено ошибкам и препятствует поддержке безопасности универсальных типов. Кроме того, рассмотрим случай, когда требуется истинное разделение данных с точки зрения мультитенантности.
Вы видите, что есть некоторые соображения, которые нужно учитывать при работе с разделами. К счастью, jOOQ предлагает различные способы работы с секционированными таблицами, и в дальнейшем мы сравним подходы, чтобы вы могли выбрать наиболее подходящий для вас.
Использование jOOQ с разметкой и мультитенантностью
Отображение схемы времени выполнения JOOQ часто используется для реализации сред баз данных, например, когда во время разработки запрашивается одна база данных, но при развертывании в рабочей среде запросы направляются в другую базу данных. Многопользовательский режим — это еще один рекомендуемый вариант использования для сопоставления схемы времени выполнения, поскольку он позволяет строго разделить разделы и настроить приложение на использование только тех баз данных или таблиц, которые настроены в сопоставлении схемы времени выполнения. Таким образом, выполнение одного и того же кода приведет к работе с разными базами данных или таблицами в зависимости от конфигурации, что позволяет обеспечить истинное разделение данных с точки зрения многопользовательского режима.
Следующая конфигурация, взятая из документации jOOQ, выполняется при создании DSLContext, поэтому ее можно рассматривать как общесистемный параметр:
Settings settings = new Settings() .withRenderMapping(new RenderMapping() .withSchemata( new MappedSchema().withInput("DEV") .withOutput("MY_BOOK_WORLD") .withTables( new MappedTable().withInput("AUTHOR") .withOutput("AUTHOR_1")))); // Add the settings to the Configuration DSLContext create = DSL.using( connection, SQLDialect.ORACLE, settings); // Run queries with the "mapped" configuration create.selectFrom(AUTHOR).fetch(); // results in SQL: // “SELECT * FROM MY_BOOK_WORLD.AUTHOR_1”
Используя этот подход, вы можете навсегда отобразить одну таблицу на один раздел, например. «AUTHOR» — «AUTHOR_1» для среды «DEV». В другой среде вы можете выбрать отображение таблицы «AUTHOR» на «AUTHOR_2».
Сопоставление со схемой выполнения позволяет отображать только одну таблицу на основе запроса, поэтому вы не можете обработать сценарий использования, в котором вы захотите манипулировать несколькими разделами таблицы. Если вы хотите иметь больше гибкости, вы можете рассмотреть следующий подход.
Использование jOOQ с разметкой и без многопользовательского режима
Если вам нужно обрабатывать несколько разделов таблицы без многопользовательского режима, вам нужен более гибкий способ доступа к разделам. В следующем примере показано, как вы можете сделать это динамическим и безопасным для типов способом, избегая ошибок и будучи использованными так же элегантно, как вы привыкли в jOOQ:
// add for(int i=1; i<=2; i++) { Builder part = forPartition(i); InsertQuery query = dsl.insertQuery(part.table(AUTHOR)); query.addValue(part.field(AUTHOR.ID), 1); query.addValue(part.field(AUTHOR.LAST_NAME), "Nowak"); query.execute(); } // select for(int i=1; i<=2; i++) { Builder part = forPartition(i); Assert.assertTrue(dsl .selectFrom(part.table(AUTHOR)) .where(part.field(AUTHOR.LAST_NAME).eq("Nowak")) .fetch() .size() == 1); }
Вы можете видеть выше, что номера разделов абстрагированы, поэтому вы можете использовать таблицу «AUTHOR» вместо «AUTHOR_1». Таким образом, ваш код не будет загрязнен многими сгенерированными классами. Другое дело, что объект разделителя инициализируется динамически, поэтому вы можете использовать его, например, в цикле, как описано выше. Также он следует шаблону Builder, так что вы можете работать с ним так, как вы привыкли к jOOQ.
Приведенный выше код делает то же самое, что и первый простой фрагмент, но есть несколько преимуществ, таких как безопасный тип и многократный доступ к многораздельным таблицам.
Интеграция разделов jOOQ без многопользовательского режима в процесс сборки Maven (необязательно)
Если вы используете Continuous-Integration, вы можете интегрировать решение выше, чтобы jOOQ не генерировал таблицы для секционированных таблиц. Это может быть достигнуто с помощью регулярного выражения, которое исключает определенные имена таблиц при создании классов Java. При использовании Maven ваша интеграция может выглядеть примерно так:
<generator> <name>org.jooq.util.DefaultGenerator</name> <database> <name>org.jooq.util.postgres.PostgresDatabase</name> <includes>.*</includes> <excludes>.*_[0-9]+</excludes> <inputSchema>${db.schema}</inputSchema> </database> <target> <packageName>com.your.company.jooq</packageName> <directory>target/generated-sources/jooq</directory> </target> </generator>
Затем он просто вызывает mvn install и плагин jOOQ maven будет генерировать схему базы данных во время компиляции.
Интеграция jOOQ с PostgreSQL: создание разделов
В этой статье описывается, как jOOQ в сочетании с функцией разделения PostgreSQL можно использовать для реализации многопользовательского режима и повышения производительности базы данных. Документация PostgreSQL гласит, что для разделения «преимущества, как правило, имеют смысл только тогда, когда таблица в противном случае будет очень большой. Точная точка, в которой таблица получит выгоду от разделения, зависит от приложения, хотя практическое правило заключается в том, что размер таблицы должен превышать физическую память сервера базы данных ».
Обеспечение поддержки разбиения с помощью jOOQ так же просто, как добавление конфигурации или небольшого служебного класса, затем jOOQ может поддерживать разбиение с использованием нескольких арендаторов или без них и без ущерба для безопасности типов. Помимо интеграции на уровне Java, описанное решение также легко интегрируется в процесс сборки и тестирования.
Возможно, вы захотите взглянуть на источники служебного класса разделителя, который также включает в себя тестовый класс, чтобы вы могли увидеть поведение и интеграцию более подробно.
Пожалуйста, дайте нам знать, если вам нужна поддержка для этой или другой интеграции JOOQ в вашей среде. UWS Software Service (UWS) является официальным партнером по интеграции jOOQ .