changed jdk on 25 springBoot on 3.5.6 and fix code
This commit is contained in:
209
src/main/resources/db/changelog/add/017-messages-chat-id.yaml
Normal file
209
src/main/resources/db/changelog/add/017-messages-chat-id.yaml
Normal file
@@ -0,0 +1,209 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 017-messages-add-chat-id
|
||||
author: krylov
|
||||
comment: >
|
||||
Перевод messages на чаты. Раньше диалоги хранились парами sender_id/receiver_id,
|
||||
теперь у сообщения есть chat_id. Колонка добавляется nullable, чтобы
|
||||
существующие строки не сломали миграцию — заполняется следующим changeset.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
not:
|
||||
columnExists:
|
||||
tableName: messages
|
||||
columnName: chat_id
|
||||
changes:
|
||||
- addColumn:
|
||||
tableName: messages
|
||||
columns:
|
||||
- column: { name: chat_id, type: BIGINT }
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-backfill-chats
|
||||
author: krylov
|
||||
comment: >
|
||||
Создаёт чат для каждой уникальной пары собеседников по старым сообщениям
|
||||
и проставляет chat_id. Данные сохраняются, история не теряется.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
tableExists:
|
||||
tableName: chats
|
||||
columnExists:
|
||||
tableName: messages
|
||||
columnName: chat_id
|
||||
changes:
|
||||
# Сначала чат на каждую пару (user_one_id < user_two_id, как это делает ChatService).
|
||||
- sql:
|
||||
splitStatements: false
|
||||
sql: |
|
||||
INSERT INTO chats (user_one_id, user_two_id, post_id, created_at)
|
||||
SELECT p.u1, p.u2, NULL, COALESCE(p.first_at, NOW())
|
||||
FROM (
|
||||
SELECT LEAST(m.sender_id, m.receiver_id) AS u1,
|
||||
GREATEST(m.sender_id, m.receiver_id) AS u2,
|
||||
MIN(m.created_at) AS first_at
|
||||
FROM messages m
|
||||
WHERE m.chat_id IS NULL
|
||||
AND m.sender_id IS NOT NULL
|
||||
AND m.receiver_id IS NOT NULL
|
||||
GROUP BY LEAST(m.sender_id, m.receiver_id), GREATEST(m.sender_id, m.receiver_id)
|
||||
) p
|
||||
ON CONFLICT (user_one_id, user_two_id) DO NOTHING
|
||||
|
||||
# Затем проставляем каждому сообщению его чат.
|
||||
- sql:
|
||||
splitStatements: false
|
||||
sql: |
|
||||
UPDATE messages m
|
||||
SET chat_id = c.id
|
||||
FROM chats c
|
||||
WHERE m.chat_id IS NULL
|
||||
AND c.user_one_id = LEAST(m.sender_id, m.receiver_id)
|
||||
AND c.user_two_id = GREATEST(m.sender_id, m.receiver_id)
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-drop-orphans
|
||||
author: krylov
|
||||
comment: >
|
||||
Сообщения, у которых нет чата: chat_id IS NULL (не удалось восстановить
|
||||
собеседника) либо чат был удалён, а сообщения остались — так было до
|
||||
появления fk_messages_chat, потому что ON DELETE CASCADE не работал.
|
||||
Такие сообщения невозможно показать в интерфейсе, они удаляются.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
columnExists:
|
||||
tableName: messages
|
||||
columnName: chat_id
|
||||
changes:
|
||||
- sql:
|
||||
splitStatements: false
|
||||
sql: |
|
||||
DELETE FROM messages m
|
||||
WHERE m.chat_id IS NULL
|
||||
OR NOT EXISTS (SELECT 1 FROM chats c WHERE c.id = m.chat_id)
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-chat-id-not-null
|
||||
author: krylov
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
columnExists:
|
||||
tableName: messages
|
||||
columnName: chat_id
|
||||
changes:
|
||||
- addNotNullConstraint:
|
||||
tableName: messages
|
||||
columnName: chat_id
|
||||
columnDataType: BIGINT
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-fk-chat
|
||||
author: krylov
|
||||
comment: >
|
||||
ON DELETE CASCADE — при удалении чата сообщения удаляются вместе с ним.
|
||||
Без каскада ChatService.deleteChat падал бы с нарушением FK.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
tableExists:
|
||||
tableName: chats
|
||||
not:
|
||||
foreignKeyConstraintExists:
|
||||
constraintName: fk_messages_chat
|
||||
changes:
|
||||
- addForeignKeyConstraint:
|
||||
baseTableName: messages
|
||||
baseColumnNames: chat_id
|
||||
referencedTableName: chats
|
||||
referencedColumnNames: id
|
||||
constraintName: fk_messages_chat
|
||||
onDelete: CASCADE
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-idx-chat-created
|
||||
author: krylov
|
||||
comment: >
|
||||
Основные запросы выборки: история чата и счётчик непрочитанных.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
not:
|
||||
indexExists:
|
||||
tableName: messages
|
||||
indexName: idx_messages_chat_created
|
||||
changes:
|
||||
- createIndex:
|
||||
tableName: messages
|
||||
indexName: idx_messages_chat_created
|
||||
columns:
|
||||
- column: { name: chat_id }
|
||||
- column: { name: created_at }
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-drop-receiver
|
||||
author: krylov
|
||||
comment: >
|
||||
receiver_id больше не используется: получатель определяется через chats.
|
||||
Сначала снимаем с него внешний ключ и индекс, иначе dropColumn не пройдёт.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
columnExists:
|
||||
tableName: messages
|
||||
columnName: receiver_id
|
||||
changes:
|
||||
- dropForeignKeyConstraint:
|
||||
baseTableName: messages
|
||||
constraintName: fk_messages_receiver
|
||||
- dropIndex:
|
||||
tableName: messages
|
||||
indexName: idx_messages_users
|
||||
- dropColumn:
|
||||
tableName: messages
|
||||
columnName: receiver_id
|
||||
|
||||
- changeSet:
|
||||
id: 017-messages-not-null
|
||||
author: krylov
|
||||
comment: >
|
||||
Сущность Message объявляет sender_id, content, is_read и created_at как
|
||||
NOT NULL, но старая схема (004) создала их nullable. Hibernate validate
|
||||
nullability не проверяет, поэтому расхождение молча оставалось.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: messages
|
||||
sqlCheck:
|
||||
expectedResult: 0
|
||||
sql: >
|
||||
SELECT COUNT(*) FROM messages
|
||||
WHERE sender_id IS NULL OR content IS NULL
|
||||
OR is_read IS NULL OR created_at IS NULL
|
||||
changes:
|
||||
- addNotNullConstraint:
|
||||
tableName: messages
|
||||
columnName: sender_id
|
||||
columnDataType: BIGINT
|
||||
- addNotNullConstraint:
|
||||
tableName: messages
|
||||
columnName: content
|
||||
columnDataType: TEXT
|
||||
- addNotNullConstraint:
|
||||
tableName: messages
|
||||
columnName: is_read
|
||||
columnDataType: BOOLEAN
|
||||
- addNotNullConstraint:
|
||||
tableName: messages
|
||||
columnName: created_at
|
||||
columnDataType: TIMESTAMP
|
||||
@@ -0,0 +1,42 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 018-posts-rules-accepted-type
|
||||
author: krylov
|
||||
comment: >
|
||||
rules_accepted объявлена в changelog 013 как boolean, но Hibernate с
|
||||
ddl-auto: update успел создать её как varchar — поле в сущности тогда
|
||||
было String. Теперь сущность использует Boolean, и ddl-auto: validate
|
||||
падал с "wrong column type". Приводим тип к схеме, приведя значения.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: posts
|
||||
columnExists:
|
||||
tableName: posts
|
||||
columnName: rules_accepted
|
||||
# Ноль boolean-колонок с таким именем = тип ещё не boolean = changeset нужен.
|
||||
sqlCheck:
|
||||
expectedResult: 0
|
||||
sql: >
|
||||
SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_name = 'posts' AND column_name = 'rules_accepted'
|
||||
AND data_type = 'boolean'
|
||||
changes:
|
||||
# В varchar могли попасть мусорные значения — приводим к true/false заранее.
|
||||
- sql:
|
||||
splitStatements: false
|
||||
sql: |
|
||||
UPDATE posts
|
||||
SET rules_accepted = CASE
|
||||
WHEN LOWER(TRIM(rules_accepted)) IN ('true', 't', '1', 'yes', 'y') THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
|
||||
- sql:
|
||||
splitStatements: false
|
||||
sql: |
|
||||
ALTER TABLE posts
|
||||
ALTER COLUMN rules_accepted DROP DEFAULT,
|
||||
ALTER COLUMN rules_accepted TYPE BOOLEAN USING rules_accepted::boolean,
|
||||
ALTER COLUMN rules_accepted SET DEFAULT false,
|
||||
ALTER COLUMN rules_accepted SET NOT NULL
|
||||
@@ -1,7 +1,15 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 004-create-chats-messages
|
||||
author: you
|
||||
id: 014-create-chats
|
||||
author: krylov
|
||||
comment: >
|
||||
Таблица чатов. Раньше создавалась Hibernate (ddl-auto: update),
|
||||
поэтому changeset идемпотентен: если таблица уже есть — MARK_RAN.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
not:
|
||||
tableExists:
|
||||
tableName: chats
|
||||
changes:
|
||||
- createTable:
|
||||
tableName: chats
|
||||
@@ -14,4 +22,43 @@ databaseChangeLog:
|
||||
- addUniqueConstraint:
|
||||
tableName: chats
|
||||
columnNames: user_one_id, user_two_id
|
||||
constraintName: uq_chat_users
|
||||
constraintName: uq_chat_users
|
||||
|
||||
- changeSet:
|
||||
id: 014-chats-idx-participants
|
||||
author: krylov
|
||||
comment: >
|
||||
findAllByUserId ищет по (user_one_id = ? OR user_two_id = ?),
|
||||
без индексов это full scan по всем чатам.
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: chats
|
||||
not:
|
||||
indexExists:
|
||||
tableName: chats
|
||||
indexName: idx_chats_user_one
|
||||
changes:
|
||||
- createIndex:
|
||||
tableName: chats
|
||||
indexName: idx_chats_user_one
|
||||
columns:
|
||||
- column: { name: user_one_id }
|
||||
|
||||
- changeSet:
|
||||
id: 014-chats-idx-participants-two
|
||||
author: krylov
|
||||
preConditions:
|
||||
onFail: MARK_RAN
|
||||
tableExists:
|
||||
tableName: chats
|
||||
not:
|
||||
indexExists:
|
||||
tableName: chats
|
||||
indexName: idx_chats_user_two
|
||||
changes:
|
||||
- createIndex:
|
||||
tableName: chats
|
||||
indexName: idx_chats_user_two
|
||||
columns:
|
||||
- column: { name: user_two_id }
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 005-drop-old-messages
|
||||
author: you
|
||||
changes:
|
||||
- dropTable:
|
||||
tableName: messages
|
||||
cascadeConstraints: true
|
||||
|
||||
- changeSet:
|
||||
id: 006-create-messages-new
|
||||
author: you
|
||||
changes:
|
||||
- createTable:
|
||||
tableName: messages
|
||||
columns:
|
||||
- column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } }
|
||||
- column: { name: chat_id, type: BIGINT, constraints: { nullable: false } }
|
||||
- column: { name: sender_id, type: BIGINT, constraints: { nullable: false } }
|
||||
- column: { name: content, type: TEXT, constraints: { nullable: false } }
|
||||
- column: { name: is_read, type: BOOLEAN, defaultValueBoolean: false }
|
||||
- column: { name: created_at, type: TIMESTAMP, constraints: { nullable: false } }
|
||||
- addForeignKeyConstraint:
|
||||
baseTableName: messages
|
||||
baseColumnNames: chat_id
|
||||
referencedTableName: chats
|
||||
referencedColumnNames: id
|
||||
constraintName: fk_messages_chat
|
||||
# УСТАРЕЛО. Файл намеренно не подключён в db.changelog-master.yaml.
|
||||
#
|
||||
# Раньше здесь был changeSet "005-drop-old-messages", который удалял таблицу
|
||||
# messages вместе со всеми сообщениями. Подключение этого файла уничтожило бы
|
||||
# переписку пользователей, поэтому он заменён на безопасную альтернативу.
|
||||
#
|
||||
# Актуальная миграция: db/changelog/add/017-messages-chat-id.yaml
|
||||
# Она добавляет messages.chat_id, создаёт чаты для старых диалогов
|
||||
# и переносит сообщения, не удаляя данные.
|
||||
#
|
||||
# Если чаты уже созданы Hibernate, 017 идемпотентна: preConditions + MARK_RAN.
|
||||
databaseChangeLog: []
|
||||
|
||||
@@ -26,3 +26,6 @@ databaseChangeLog:
|
||||
- include: { file: db/changelog/alter/013-del-ad_id-click-impression.yaml }
|
||||
- include: { file: db/changelog/create/011-create-fullscreen-ads.yaml }
|
||||
- include: { file: db/changelog/constraint/010-idx-post-lat-lng-index.yaml }
|
||||
- include: { file: db/changelog/create/006-create-chats.yaml }
|
||||
- include: { file: db/changelog/add/017-messages-chat-id.yaml }
|
||||
- include: { file: db/changelog/alter/014-posts-rules-accepted-type.yaml }
|
||||
|
||||
Reference in New Issue
Block a user