Используете ли вы ограничение unique в классах предметной области Grails?

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

Возьмем небольшой пример, который очень распространен, то есть домен User, который содержит данные (например, мобильный телефон, адрес электронной почты и пароль) пользователя, который может войти в ваше приложение.

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

Когда вы запустите приложение и увидите его в базе данных (я беру пример с MySQL), вы увидите, что на уровне базы данных применяется уникальное ограничение для столбца email & mobile.

Теперь давайте вставим запись в базу данных, запустив http://localhost:8080/application/save:

Когда мы запустим код, новая запись будет записана в базе данных.

Теперь давайте снова запустим тот же код (обновите эту страницу). Что ты увидишь?

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

Теперь посмотрим журналы в консоли (я включил подробное ведение журнала запросов Hibernate в моем приложении Grails, используя один из наших основных плагинов - ядро):

Ого! Что это за 3 дополнительных запроса к базе данных, помимо запроса на вставку?

Это обратная сторона магии, которую творит Грааль (GORM).

По сути, всякий раз, когда вы сохраняете экземпляр домена с каким-либо уникальным ограничением, Grails выполняет тихий и отдельный вызов базы данных , чтобы убедиться, что в базе данных есть записи с таким же значением. И если запись с таким же значением существует, она не выполняет операцию сохранения и заполняет ошибку проверки, как показано в приведенных выше журналах.

Итак, в нашем примере, когда мы выполнили код 2 раза, он сначала проверил (используя два отдельных вызова базы данных), существует ли уже электронное письмо с [email protected] и мобильным с 9876543210.

Это означает, что каждая операция вставки в базу данных выполняет N дополнительных запросов, если в доменном классе Grails имеется N уникальных ограничений.

Что произойдет, когда мы обновим доменный класс?

Если вы обновили значения полей email или mobile и пытаетесь сохранить, то Grails снова выполнит вызовы базы данных (перед запросом на обновление), чтобы проверить, существует ли уже запись с обновленным адресом электронной почты или мобильного телефона, что очевидно, поскольку вы обновили ценности.

Но если вы не обновляли значения этих полей, тогда не будет отдельных вызовов для проверки уникального ограничения.

Давайте рассмотрим другой вариант использования, который разработчики использовали для большего контроля.

Здесь я добавил отдельный вызов для проверки существования пользователя, который сделает один вызов для проверки уникальных значений.

Таким образом, технически мы уже проверили, существует ли пользователь уже или нет (следовательно, 1 вызов базы данных уже выполнен). Теперь Grails снова выполнит 2 вызова базы данных перед вставкой записи в базу данных. Это означает,

Всего 2 дополнительных вызова базы данных перед вставкой записи.

Из этих трех два вызова базы данных (сделанные Grails) бесполезны, потому что вы абсолютно уверены, что пользователя с такой же записью не существует. Теперь подумайте, ваше приложение получает 1000 пользователей в неделю,

Это означает, что в вашем приложении выполняются дополнительные 2000 запросов к базе данных 😲

Каждый отдельный вызов базы данных оплачивается вашим ежемесячным счетом, потому что он добавляет обратный путь от сервера приложений к серверу базы данных. Это означает, что 2000 дополнительных обращений к базе данных могут обернуться огромным ежемесячным счетом от вашего облачного провайдера.

Какое решение сохранить вызовы базы данных?

Что ж, это не проблема, которую нужно решать. Это мощная функция с учетом моего Grails, но как разработчик Grails вы ДОЛЖНЫ знать об этих дополнительных вызовах базы данных, потому что сила приходит с ответственностью и осторожностью.

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

Что ж, обходной путь прост:

Используйте uniquemapping вместо constraints

Какие? Это так просто? 😮😲

Да, вы прочитали это прямо в заголовке - Измените одну строку, чтобы значительно повысить производительность вашего приложения.

Когда вы определяете свойство сопоставления уникальности, ограничение уникальности будет по-прежнему применяться на уровне базы данных, но Grails не будет выполнять дополнительные запросы к базе данных для подтверждения уникальных значений. Посмотрим, как это работает:

(Проверьте в своей БД, что уникальное ограничение все еще применяется на уровне базы данных).

Теперь давайте выполним наш код и посмотрим на результат:

Удивлен !! Хммм

Да, это сохранило эти 2 вызова базы данных (отсюда 2000 вызовов для нашего примера). Ваше здоровье! 🎉🎊

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

Есть момент, о котором нужно позаботиться при использовании mapping вместо constraints

По сути, когда вы не используете constraints, это означает, что Grails не будет защищать уникальное ограничение, и если вы явно не проверите его, MySQL выдаст исключение, когда Grails попытается вставить запись и будет деликатность.

Известное исключение - MySQLIntegrityConstraintViolationException.

Поэтому учитывайте эти моменты, когда вам нужно выбирать междуconstraints или mapping:

Когда использовать constraints:

  1. Когда вы знаете, что в доменном классе будет очень мало операций вставки, имеющих уникальные ограничения.
  2. Если вы не добавили ручную / явную проверку (или запрос), чтобы убедиться, что нет повторяющихся записей.

Когда использовать mapping:

  1. Когда вы добавили какую-либо ручную / явную проверку, чтобы убедиться, что нет повторяющихся записей.
  2. Когда вы абсолютно уверены, что операция вставки будет только уникальной (например, столбец, имеющий уникальные значения из класса UUID).
  3. Когда количество уникальных ограничений больше 1, об этом можно позаботиться с помощью явных вызовов (как в нашем примере).

Хотите увидеть свою потрясающую идею в действии? Или хотите к нам присоединиться? Свяжитесь с нами на https://www.wizpanda.com/

(Вы используете плагин миграции базы данных Grails? Также ознакомьтесь с этой статьей https://medium.com/wizpanda/custom-groovy-changes-using-grails-db-migration-plugin-part-i-c51d4ee96a6e)