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

Изменения схемы базы данных при переходе в спящий режим и весенней загрузке

Целевая аудитория Эта статья была написана для читателей, имеющих опыт работы с Java, H… С тегами java, spring, hibernate, database.

Целевая аудитория

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

Вступление

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

В этой статье – первой в серии – мы сосредоточимся на лучших практиках отрасли и функции автоматической генерации схемы Hibernate . Мы объясним, что мы узнали из него и где его можно использовать.

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

Установка

Давайте сначала создадим новую схему базы данных с именем адресная книга с использованием клиента командной строки MySQL :

>mysql -u santa -p
Enter password: ******
mysql> CREATE DATABASE addressBook;
Query OK, 1 row affected (0.12 sec)

Давайте теперь откроем наше Java-приложение , которое использует Spring Boot и MySQL . Конфигурации для MySQL можно найти внутри application.yml |/:

spring:
  jpa:
    database: mysql
    hibernate:
      ddl-auto: update
  datasource:
    url: jdbc:mysql://localhost:3306/addressBook
    username: santa
    password: secret

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

ddl-auto: update показывает, что наша схема MySQL должна обновляться при запуске приложения (будет обсуждаться в следующем параграфе).

Создание схемы базы данных с нуля

На данном этапе только что была создана схема нашей базы данных. В нашем приложении есть только один класс сущностей с именем User .

@Entity @Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String firstName;
    private String lastName;
    private LocalDate dateOfBirth;
}

Примечание: аннотация @Data берется из Lombok и автоматически генерирует наши методы получения/установки.

Как показано в предыдущем разделе, мы настроили автоматическое обновление схемы базы данных следующим образом:

spring.jpa.hibernate.ddl-auto: update

Давайте запустим наш JUnit набор тестов:

mvn clean test

В журналах мы видим, что был выполнен следующий запрос к базе данных:

create table user (
id integer not null auto_increment, 
date_of_birth date, 
first_name varchar(255), 
last_name varchar(255), 
primary key (id)) engine=InnoDB

Как выполняются тесты с помощью Hibernate ?

При запуске Hibernate анализирует все классы, которые были оформлены аннотацией @Entity . Затем он сканирует класс User и генерирует запрос на создание таблицы SQL . Имя таблицы, имена столбцов, типы и т.д. Основаны на информации, содержащейся в пользовательском классе (имя класса, имена и типы атрибутов, аннотации и т.д.).

Запуск приложения еще раз

Создана схема базы данных AddressBook , содержащая таблицу User .

Какого поведения мы должны ожидать, когда запустим приложение еще раз?

Когда Hibernate снова запускает тесты, он сравнивает класс Пользователь против таблицы пользователь . Затем он видит, что класс и таблица синхронизированы и это не вносит никаких дальнейших изменений.

Какой SQL?

В то время как SQL выглядит одинаково при работе с различными поставщиками баз данных, не существует такого понятия, как полностью совместимый SQL . Существуют тонкие различия в том, как каждый SQL обрабатывает даты, конкатенацию строк и т.д. Hibernate элегантно абстрагирует эти различия как “диалекты”.

Внутри вашего pom.xml мы настроили драйвер mysql jdbc в качестве зависимости для MySQL 8. Пружинный Ботинок затем предполагается, что мы используем значение по умолчанию MySQL 8 диалект и настраивает Гибернацию соответственно, как показано в журналах запуска:

HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect

Добавление изменений в существующую базу данных

Давайте теперь добавим Адрес сущности для нашей модели.

@Data @Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String streetAddress;
    private String zipCode;
    private String city;
    //...
}

Мы также добавляем связь от User к Адрес следующим образом:

@Entity @Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String firstName;
    private String lastName;
    private LocalDate dateOfBirth;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name="FK_USER_ID"))
    private List
addressList = new ArrayList<>(); }

Когда приложение запускается (все еще в режиме автоматического обновления ), Hibernate создает таблицу address следующим образом:

create table address (
       id integer not null auto_increment,
        city varchar(255),
        street_address varchar(255),
        zip_code varchar(255),
        user_id integer,
        primary key (id)
    ) engine=InnoDB

 alter table address 
       add constraint FK_USER_ID 
       foreign key (user_id) 
       references user (id)

Таблица address и ее связь с user были добавлены, как и ожидалось. В то время как Hibernate ‘s auto-update большую часть времени работает нормально, он довольно волшебный и подвержен ошибкам. По нашему опыту, легко переименовать класс или поле, а затем забыть о том факте, что при следующем развертывании приложения будет сгенерирована новая таблица или столбец.

В следующем разделе мы обсудим рекомендации и меры предосторожности при внесении изменений в схему рабочей базы данных.

Автоматическое обновление схемы в рабочей среде?

В своей официальной документации команда Hibernate рекомендует следующее:

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

Вот подход, который мы обычно используем:

База данных H2 MySQL MySQL MySQL
Настройка автоматического обновления в режиме гибернации создать-удалить обновление утверждать утверждать
Резервное копирование БД никто никто mysqldump mysqldump
  • Для модульных тестов мы используем H2 . Вся база данных создается в памяти во время запуска и удаляется после выполнения всех тестов ( create-drop ).
  • При запуске локального веб-приложения (на localhost ) мы запускаем update и копируем из журналов все сгенерированные сценарии обновления (например, для адресной таблицы в нашем примере). Мы будем повторно использовать эти скрипты для нашего промежуточного и производственные среды.
  • В постановке и производственных сред, мы используем следующую настройку:
spring.jpa.hibernate.ddl-auto=validate

Во время запуска Hibernate проверяет s, что схема базы данных совместима с нашим сопоставлением JPA/Hibernate . Если какой-либо класс или атрибут не сопоставлен должным образом, Hibernate выдает исключение, и приложение не запускается. Мы пытаемся воспроизвести поведение, которое мы будем иметь в рабочей среде, поэтому мы обновляем нашу схему вручную, используя сценарии, собранные в нашей локальной среде разработки.

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

Примечание: Вы можете видеть, что предлагаемые процессы одинаковы для staging и производственные среды. Нарушение промежуточной среды нашего приложения не должно иметь большого значения. Однако это возможность выполнить пробный запуск, прежде чем обновлять схему нашей базы данных в рабочей среде.

Добавление конфликтующего изменения

Конфликтующее изменение – это изменение , которое включает переименование таблицы или столбца . Давайте представим, например, что address , который используется в нашей существующей схеме, недостаточно конкретен, и что мы хотели бы переименовать таблицу address в почтовый адрес . Давайте изменим название Адрес класс следующим образом:

@Data @Entity
public class PostalAddress {
    //...
}

Функция гибернации автоматического обновления плохо работает с конфликтующими изменениями. Если мы перезапустим наше приложение в режиме update , оно создаст новую таблицу с именем postal_address и по-прежнему сохраняет существующую таблицу address .

Давайте отключим автоматическую схему update и вместо этого используем validate , как описано в предыдущем абзаце:

spring.jpa.hibernate.ddl-auto: validate

При запуске приложения Hibernate обнаружит, что наши классы не синхронизированы со схемой базы данных, и выдаст следующее исключение:

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: 
Schema-validation: missing table [postal_address]
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator
    .validateTable(AbstractSchemaValidator.java:121)

Чтобы избежать этой проблемы, нам нужно остановить приложение и запустить приведенный ниже сценарий до развертывания новой версии приложения:

mysqldump --defaults-file="/var/.../extraparams.cnf"  ... 
>mysql -u santa -p
Enter password: ******
mysql> use addressBook; -- choose the database schema to be used
mysql> RENAME TABLE address to postal_address;

Теперь мы изменили имя нашей таблицы и развернули обновленную версию приложения.

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

Вывод

Мы видели, что Hibernate ‘s auto-update является отличным инструментом разработки и не должен использоваться в промежуточной и рабочей среде. В процессе подготовки и производства мы видели, что вы можете запускать свои sql-запросы вручную. В нашем последующем блоге (который будет опубликован к 1 марта 2020 года) мы обсудим, как использовать Liquibase в качестве инструмента миграции схемы базы данных для ваших промежуточных и производственных сред.

Спасибо, что прочитали наш блог!

Майкл Мертв. (спасибо моим коллегам Николасу Гиньяру, Лью Мин Шаню и многим другим за рецензирование этой статьи!).

Оригинал: “https://dev.to/onlinepajak/database-schema-changes-with-hibernate-and-spring-boot-3f5k”