7 Ocak 2021 Perşembe

SpringData Flyway Kullanımı

Giriş
flyway'i komut satırından kullanabilmek için kurmak gerekir. Kurma işlemi aslında sadece zip dosyasını indirip açmak ve Path'e eklemekten ibaret. Şöyle yaparız
export PATH=$PATH:$HOME/flyway-9.3.0
Komut satırından 7 seçenek kullanılabilir.
migrate
clean
info
validate
undo
baseline
repair

migrate seçeneği
Şöyle yaparız
flyway migrate -configFiles=flyway.properties
undo seçeneği
Şöyle yaparız
flyway undo -configFiles=flyway.properties
Docker Olarak Kullanım
Örnek
compose.yaml dosyasında şöyle yaparız. Projeye Docker Compose Desteği ekleyince flyway otomatik çalışır
# FILE: compose.yaml

version: '3'
services:
  postgres:
    image: 'postgres:15'
    container_name: "postgres"
    environment:
      - 'POSTGRES_DB=postgres'
      - 'POSTGRES_PASSWORD=postgres'
      - 'POSTGRES_USER=postgres'
    ports:
      - '5432:5432'
  flyway:
    # Use Docker image containing Flyway CLI
    image: flyway/flyway:9.22.1
    container_name: "flyway-migration"
    # Execute migration command with input parameters on container startup
    command: -locations=filesystem:/flyway/migration -user=postgres -password=postgres -url="jdbc:postgresql://postgres:5432/postgres" -connectRetries=5 migrate
    # Copy migration script folder into container
    volumes:
      - ./db/migration:/flyway/migration
    # Wait for Postgres container to start
    depends_on:
      - postgres

Kütüphane Olarak Kullanım
1. Flyway maven veya gradle dependency eklenir
2. Ayrıca "spring.jpa.hibernate.ddl-auto : none" olmalıdır
3. application.properties dosyasında data source tanımlanır. Flyway için herhangi bir ayar belirtmemize gerek yok. Varsayılan ayarlar yeterli.

Örnek
Şöyle yaparız
server:
  port: ${port:8080}

spring:
  application:
    name: flyway-demo

  datasource:
    driver-class-name: org.h2.Driver
    username: sa
    password:
    url: "jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE"
  h2:
    console:
      enabled: true
      path: /h2-console
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: none

Maven
Şöyle yaparız. Böylece uygulama çalışırken schema migration (şema taşınması/değiştirilmesi) yapılabilir.
<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
  <version>6.5.5</version>
</dependency>
Açıklaması şöyle
With this, Flyway automatically scans the classpath looking for the migration scripts, and executes them against the database during application startup. By default, it scans for files located in classpath:db/migration.

flyway_schema_history Tablosu
Açıklaması şöyle
Flyway creates a special table called the “Flyway Schema History” to store metadata, such as script name, migration version, execution status, execution date-time, etc.

Then, to keep track of the schema evolution, it logs the metadata into this table, leaving the Flyway Schema History table as the only source of truth, and allowing Flyway to check if a new migration script has been added.

In addition, it validates the integrity of older scripts using a checksum stored in the table. So, if any of the scripts in the table has been modified, the application will fail to start.
Şeklen şöyle. Bu tabloyu flyway ilk kez çalışırken yaratır. Değişiklikleri bu tabloda takip eder. Böylece bir script ikinci kez çalıştırılmaz
Tablonun satırları şöyle
| installed_rank | version | description  | type | script                 | checksum   | installed_by | installed_on   | execution_time | success |
|----------------|---------|--------------|------|------------------------|------------|--------------|----------------|----------------|---------|
| 1              | 1       | post tag     | SQL  | V1_0__post_tag.sql     | -611721954 | postgres     | 30-06-20 15:21 | 61             | TRUE    |
| 2              | 1.1     | post details | SQL  | V1_1__post_details.sql | 511495203  | postgres     | 30-06-20 15:21 | 13             | TRUE    |
| 3              | 1.2     | post comment | SQL  | V1_2__post_comment.sql | 762350400  | postgres     | 30-06-20 15:21 | 14             | TRUE    |
| 4              | 1.3     | users        | SQL  | V1_3__users.sql        | -596399497 | postgres     | 30-06-20 15:55 | 32             | TRUE    |

Checksum Hatası
Hata şöylee
org.flywaydb.core.api.FlywayException: Validate failed: Migration checksum mismatch for migration version .
Açıklaması şöyle
What is it?
The checksum validation from Flyway is basically a check between the checksum of the current migration file in your app against the checksum from the same migration it already run in the past. You can check this list on your database, under flyway_schema_history table created and used by Flyway.

What it means?
It means that the script you app has when it starts is not the same Flyway already applied in the past and since it can't figure out if that is correct or not, it fails. Ideally, you should never change a script you already applied, you should always evolve and create new ones, that's the whole idea about migrations.

How to avoid it?
As said before, you should never change scripts that were already executed before. You should always create new ones. Of course if that happens on a dev environment and you figure out changes are needed.
application.properties
application.properties yazısına taşıdım

Script Dizini
Veri tabanı scriptleri için varsayılan dizin şöyledir
src/resources/db/migration
Çift alt çizgi kullanılması gerekir. Açıklaması şöyle
Make sure you have double underscores present after the version of a file, i.e “V1__FILENAME.sql”, if you provide a single underscore after the version file is skipped when starting the application by Flyway, i.e “V1_FILENAME.sql”.

V1__create_book_table.sql
V1.1__insert_into_books.sql
Şeklen şöyle
V ile başlayanlar dışında U ve R ile de başlayan dosya isimleri olabilir. Şeklen şöyle

Açıklaması şöyle
Part 1: It is the letter “v” in uppercase. The name always starts with this letter.
Part 2: It is the migration version; it can be 1, 001, 1.2.3, 2021.09.24.12.55.32, … you got it.
Part 3: It is the two underscores (_)
Part 4: The description of the migration; you can separate words with an underscore or a space.
Part 5: It is the extension of the file .sql
Açıklaması şöyle
The Part 1, 3 and 5 are configurable using the configuration file with this properties: spring.flyway.sql-migration-prefix, spring.flyway.sql-migration-separator, spring.flyway.sql-migration-suffixes.

spring.flyway.sql-migration-prefix=T
spring.flyway.sql-migration-separator=--
spring.flyway.sql-migration-suffixes=.blog
The migration file will be: T1.0 — create_users_table.blog
Açıklaması şöyle.
sql statement should end with ‘;’ or cause execute fail.
Örnek - V1.0__initial_schema.sql
Şöyledir. Burada V1.0 ismi kullanılıyor
-- V1.0__initial_schema.sql CREATE TABLE author ( ID BIGINT NOT NULL AUTO_INCREMENT, NAME VARCHAR(100) NOT NULL UNIQUE, BIRTH_YEAR INT NOT NULL, CONSTRAINT pk_author PRIMARY KEY (ID) ); CREATE TABLE book ( ID BIGINT NOT NULL AUTO_INCREMENT, TITLE VARCHAR(150) NULL, PUB_DATE datetime NULL, AUTHOR BIGINT NULL, CONSTRAINT pk_book PRIMARY KEY (ID) ); ALTER TABLE book ADD CONSTRAINT uc_book_title UNIQUE (TITLE); ALTER TABLE book ADD CONSTRAINT FK_BOOK_ON_AUTHOR FOREIGN KEY (AUTHOR) REFERENCES author (ID);
Örnek - V1_create_person.sql
ŞöyledirBurada V1 ismi kullanılıyor
create table person
(
    id           serial primary key,
    first_name   text,
    last_name    text,
    date_created timestamp with time zone
);
Örnek
Eğer uygulamayı çalıştırmadan sadece Flyway işini yapsın istersek şöyle yaparız
import static org.springframework.boot.WebApplicationType.NONE;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Import;

/**
 * Utility to run flyway migration without starting service
 */
@SpringBootConfiguration
@Import({DataSourceAutoConfiguration.class, FlywayAutoConfiguration.class})
public class FlywayMigrationRunner {

  public static void main(String[] args) {

    SpringApplication application =
        new SpringApplicationBuilder(FlywayMigrationRunner.class)
            .web(NONE).build();

    application.run(args);
  }
}
Örnek - Test
Eğer Flyway'i testlerde kullanmak istermezsek şöyle yaparız. application-test-containers.yml içinde flyway'i kapatırız
spring:
  datasource:
    url: jdbc:tc:postgresql:9.6.8:///test_database
    username: user
    password: password
  jpa:
    hibernate:
      ddl-auto: create
  flyway:
    enabled: false
application-test-containers-flyway.yml içinde yeni konfigürasyon yazarız
spring:
  datasource:
    url: jdbc:tc:postgresql:9.6.8:///test_database
    username: user
    password: password
Test içinde ActiveProfile ile kullanırız
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@Testcontainers
@ActiveProfiles("test-containers-flyway")
class PersonCreateServiceImplTestContainersFlyway {
  @Autowired
  private PersonRepository personRepository;
  @MockBean
  private PersonValidateService personValidateService;
  @Autowired
  private PersonCreateService personCreateService;

  @BeforeEach
  void init() {
    personRepository.deleteAll();
  }
  @Test
  void shouldCreateOnePerson() {
    final var people = personCreateService.createFamily(
        List.of("Simon"),"Kirekov");
    assertEquals(1, people.size());
    final var person = people.get(0);
    assertEquals("Simon", person.getFirstName());
    assertEquals("Kirekov", person.getLastName());
    assertTrue(person.getDateCreated().isBefore(ZonedDateTime.now()));
  }
  @Test
  void shouldRollbackIfAnyUserIsNotValidated() {
    doThrow(new ValidationFailedException(""))
        .when(personValidateService)
        .checkUserCreation("John", "Brown");
    assertThrows(ValidationFailedException.class, () -> personCreateService.createFamily(
        List.of("Matilda", "Vasya", "John"),
        "Brown"
    ));
    assertEquals(0, personRepository.count());
  }
}


Hiç yorum yok:

Yorum Gönder