Рубрики
Без рубрики

Как использовать строку UUID в режиме гибернации с MySQL

Важно использовать уникальный идентификатор для столбцов в таблице. Помимо опции автоматического увеличения, мы можем использовать столбец Java UUID, однако, если мы хотим, чтобы он был удобочитаемым, нам нужно сохранить его в столбце VARCHAR. Это можно легко сделать с помощью аннотации @Type в режиме гибернации

Автор оригинала: Petre Popescu.

При создании структуры базы данных важно убедиться, что каждая строка в таблице имеет уникальный идентификатор, чтобы ее можно было легко индексировать, извлекать и манипулировать ею при необходимости. Наиболее распространенными методами являются использование автоматически увеличивающегося столбца или сгенерированного UUID.

Я не буду описывать метод автоматического увеличения, так как он не создает реальных проблем и может быть сопоставлен целому числу в классе домена Java. При использовании UUID это может создать некоторые проблемы, в зависимости от конфигурации и версии MySQL. Хорошей новостью является то, что существует простой способ сопоставления столбца Java UUID с MySQL с помощью Hibernate, и никаких дополнительных библиотек не требуется.

Во-первых, давайте посмотрим на таблицу образцов. Я буду использовать упрощенную версию таблицы ПОЛЬЗОВАТЕЛЕЙ, в которой хранится информация для входа каждого зарегистрированного пользователя на сайт. Нам понадобится следующая информация:

  • ИДЕНТИФИКАТОР
  • Электронная почта
  • Имя
  • Хэш Пароля

Это упрощенная структура с минимальным количеством столбцов. Хранение несоленого хэша пароля не рекомендуется. Ознакомьтесь с рекомендациями по хранению паролей в базе данных при создании окончательной структуры.

Мы хотим, чтобы идентификатор был уникальным, поэтому мы будем использовать UUID Java в нашем объекте домена. Поскольку мы хотим, чтобы вся информация также была удобочитаемой для человека, идентификатор должен храниться в виде строки. Поскольку мы знаем формат UUID, когда он представлен в виде строки, мы знаем, что он имеет длину 36 символов, поэтому мы можем определить столбец как VARCHAR(36).

Теперь мы создаем наш доменный класс, используя аннотации Hibernate, чтобы сопоставить его с нашей существующей таблицей MySQL.

@Entity
@Table(name = "users")
public class UserDO {
    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column(name = "id", updatable = false, nullable = false, columnDefinition = "VARCHAR(36)")
    private UUID id;

    @Column
    private String username;

    @Column
    private String email;

    @Column(name = "password_hash")
    private String passwordHash;
}

Мы ожидаем, что все работает так, как задумывалось, и что Hibernate знает, как сопоставить UUID со столбцом VARCHAR(36), но это верно только частично. При выполнении кода и попытке выполнить вставку, java.sql. Будет создано исключение SQLException:

java.sql.SQLException: Incorrect string value: '\xE3\xAF\xF7d\x0CG…' for column 'id' at row 1
         at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
         at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
         at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
         at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
         at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092)
         at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040)
         at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347)
         at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025)
         at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
         at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
         at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)

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

Мы хотим, чтобы столбец String UUID использовался в режиме гибернации без необходимости каких-либо дальнейших манипуляций в коде. Вот где вступает в действие новая аннотация: @Type

@Entity
@Table(name = "users")
public class UserDO {
    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column(name = "id", updatable = false, nullable = false, columnDefinition = "VARCHAR(36)")
    @Type(type = "uuid-char")
    private UUID id;

    @Column
    private String username;

    @Column
    private String email;

    @Column(name = "password_hash")
    private String passwordHash;
}

Эта аннотация определяет сопоставление типов гибернации. Использование “uuid-char” предписывает Hibernate хранить UUID в виде строки, а не двоичного значения. Таким образом, он может быть записан и прочитан из столбца VARCHAR(36) без необходимости какой-либо предварительной обработки с нашей стороны.

Статья, первоначально опубликованная на моем личном веб-сайте по адресу Как использовать строковые идентификаторы UUID в режиме гибернации

Оригинал: “https://www.codementor.io/@petrepopescu/how-to-use-string-uuid-in-hibernate-with-mysql-1jrhjh6ef5”