Программа прочитала из базы данных некую сущность Entity, и показала ее пользователю для редактирования.
Пользователь долго думает, изменяет ее свойства и нажимает кнопку "Записать".
Однако, за это время эту Сущность уже могли отредактировать в БД другие пользователи.
То есть наш пользователь видит неактуальные данные, и принимает решение о редактировании и записи Сущности на основе неактуальных данных.
Как должна делать программа в таких случаях?
Должна ли программа оповещать пользователя при изменении данных Сущности в БД?
Должна ли программа проверять актуальность редактируемых свойств Сущности непосредственно перед записью в БД новых значений этих свойств?
Z>Как должна делать программа в таких случаях?
Разрешать конфликт — как зависит от бизнес-требований. В простейшем случае отменять изменения пользователя и просить сделать ещё раз.
Z>Должна ли программа оповещать пользователя при изменении данных Сущности в БД?
Зависит от бизнес-требований
Z>Должна ли программа проверять актуальность редактируемых свойств Сущности непосредственно перед записью в БД новых значений этих свойств?
Да, притом с ACID.
Z>Объясните, пожалуйста. https://en.wikipedia.org/wiki/Optimistic_concurrency_control
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, zelenprog, Вы писали:
Z>Объясните, пожалуйста.
Это не забота программы, она все равно это не сможет реализовать это со 100% надежностью.
Это забота базы данных. Забота программы — выбрать подходящую стратегию блокирования (optimistic, pessimistic, etc)
Т.е. в идеале эту ситуацию должна ловить база, а в программе просто будет валиться ошибка в случае если текущий пользователь пытается перезаписать данные другого пользователя.
Ситуация которую ты описал, это в чистом виде перезаписывание данных введенных другим пользователем.
Здравствуйте, zelenprog, Вы писали:
Z>Должна ли программа оповещать пользователя при изменении данных Сущности в БД?
Да
Z>Должна ли программа проверять актуальность редактируемых свойств Сущности непосредственно перед записью в БД новых значений этих свойств?
Да
Z>Объясните, пожалуйста.
Обычно выдается сообщение типа: "Данные, которые вы отредактировали изменены другим пользователем."
Второй вариант: блокировать на уровне субд типа "select for update", тогда пока первый не откоммитится, никто больше изменить данные не сможет.
Z>>Как должна делать программа в таких случаях? ·>Разрешать конфликт — как зависит от бизнес-требований. В простейшем случае отменять изменения пользователя и просить сделать ещё раз.
Z>>Должна ли программа проверять актуальность редактируемых свойств Сущности непосредственно перед записью в БД новых значений этих свойств? ·>Да, притом с ACID.
Z>>Должна ли программа проверять актуальность редактируемых свойств Сущности непосредственно перед записью в БД новых значений этих свойств? DO>Да DO>Обычно выдается сообщение типа: "Данные, которые вы отредактировали изменены другим пользователем."
Суть понял.
Поясните, пожалуйста, чуть подробнее как надо сделать в следующей ситуации.
1) Читаем из базы данных таблицу товаров.
Эта таблица (назовем ее "исходная") хранится в оперативной памяти (в качестве хеша) для более комфортной работы пользователя.
2) Для отображения пользователю, все строки этой таблицы копируются в новую таблицу, но колонки копируются не все.
Некоторые поля в новой таблице форматируются. И новая таблица отображается на форме пользователю. Это "пользовательская" таблица.
Соответствие между "исходной" и "пользовательской" таблицами один-к-одному по специальному ключу.
3) Пользователь просматривает свою таблицу товаров и корректирует в ней значения.
При корректировке для валидации используется "оригинальная" таблица, так как она содержит полную информацию по товару:
— программа находит строку "исходной" таблицы, соответствующую текущей строке "пользовательской" таблицы
— проверять корректность и допустимость изменений
4) Пользователь нажимает "Записать изменения".
Что должна сделать программа перед записью для проверки актуальности отредактированных сведений?
Правильно ли я понял, что программа должна сделать следующее:
1) по каждой отредактированной строке программа читает свежие данные из базы
2) сравнивает свежие данные с данными в строке "оригинальной" таблицы
3) если данные отличаются — выдается ошибка, процесс прерывается
4) если данные совпадают — записываем указанные пользователем сведения
5) строку "оригинальной" таблицы заполняем новыми сведениями, которые были только что записаны
Здравствуйте, zelenprog, Вы писали:
Z>Что должна сделать программа перед записью для проверки актуальности отредактированных сведений? Z>Правильно ли я понял, что программа должна сделать следующее: Z>1) по каждой отредактированной строке программа читает свежие данные из базы Z>2) сравнивает свежие данные с данными в строке "оригинальной" таблицы Z>3) если данные отличаются — выдается ошибка, процесс прерывается Z>4) если данные совпадают — записываем указанные пользователем сведения Z>5) строку "оригинальной" таблицы заполняем новыми сведениями, которые были только что записаны Z>Верно?
Есть три версии записи:
1. В БД, назовем ее ACTUAL
2. Запрошенная пользователем, назовем ее OLD
3. Измененная пользователем, назовем ее NEW
Сравнивать нужно ACTUAL с OLD, если они не равны, значит кто-то уже успел откоммитится первым и изменять запись (накатывать NEW на ACTUAL) нельзя, об этом нужно сообщить пользователю.
DO>Есть три версии записи: DO>1. В БД, назовем ее ACTUAL DO>2. Запрошенная пользователем, назовем ее OLD DO>3. Измененная пользователем, назовем ее NEW DO>Сравнивать нужно ACTUAL с OLD, если они не равны, значит кто-то уже успел откоммитится первым и изменять запись (накатывать NEW на ACTUAL) нельзя, об этом нужно сообщить пользователю.
Это я понял.
Теперь меня интересует чуть более подробный алгоритм этих действий.
Тот алгоритм, который я описал — правильный?
Вот еще раз напишу.
Есть таблица товаров, прочитанная из БД. Есть копия этой таблицы, которая отображается пользователю на форме, которую он редактирует.
То есть имеем: "оригинальная" таблица, и "пользовательская" редактируемая таблица на форме.
Пользователь отредактировал какие-то записи и какие-то поля в таблице, нажимает "Записать".
Программа должна сделать следующие действия для каждой измененной записи таблицы:
1) прочитать свежие ACTUAL данные из базы
2) сравнить свежие данные с OLD данными в строке "оригинальной" таблицы
3) если данные отличаются — выдать ошибку, процесс прерывается
4) если данные совпадают — записать указанные пользователем сведения
5) строку "оригинальной" таблицы заполнить новыми сведениями, которые были только что записаны
Все правильно?
Обращаю внимание на пункт (5). "Оригинальную" таблицу нужно же обновить?
Верно?
Здравствуйте, zelenprog, Вы писали:
Z>Обращаю внимание на пункт (5). "Оригинальную" таблицу нужно же обновить?
Зачем? Если ACTUAL = OLD, просто накатываются изменения в БД, и делается запрос актуальных данных из БД.
Z>>Обращаю внимание на пункт (5). "Оригинальную" таблицу нужно же обновить?
DO>Зачем?
Как зачем? Пользователь ведь дальше будет продолжать работать с программой.
А наша "оригинальная" таблица после записи содержит сведения, которые были прочитаны из базы в самом начале.
Значит, каждую запись, записанную в БД, надо обновить и в "оригинальной" таблице. Она же нам и в следующий раз будет нужна, чтобы сравнить актуальные данные с прочитанными ранее.
DO>Зачем? Если ACTUAL = OLD, просто накатываются изменения в БД, и делается запрос актуальных данных из БД.
"и делается запрос актуальных данных из БД" — а запрос каких данных? Всех?
Зачем из базы читать все данные, если например пользователь отредактировал и записал всего две записи?
Здравствуйте, zelenprog, Вы писали:
Z>"и делается запрос актуальных данных из БД" — а запрос каких данных? Всех?
— А потом вас там публично выпорют как бродяг и отправят в Сибирь убирать снег!
— Весь?
— Да
(Навеяло)
Здравствуйте, zelenprog, Вы писали:
Z>Объясните, пожалуйста.
В ынтерпрайзе есть 2 классических стратегии решения данной задачи, стандартной для ынтрпрайза:
— оптимистичная блокировка
— пессимистичная блокировка
Вообще кури ынтрпрайз (например, учебник гонзалвеса "изучаем jee"), там многие типовые задачи имеют стандартные проработанные решения на уровне спецификаций, чтоб ты свои кривые велосипеды не изобретал. А фреймворки типа Spring вообще имеют спектр модулей ещё шире чем кондовый ынтрпрайз, так что Spring тоже учить надо. Для расширения сознания без веществ ещё рекомендую книгу "jpa api и hibernate"
версионирование записей БД. специальное поле которое увеличевается на 1 при обновлении.
Если при записи у вас версия меньше,значит кто-то уже успел изменить данные.
Дальше уже по желанию. тупо перезаписать. построить диалог с пользователем.
И да, вроде как подобный механизм есть в самих СУБД Multi version concurrency. Это надо по докам смотреть.
Р>версионирование записей БД. специальное поле которое увеличевается на 1 при обновлении. Р>Если при записи у вас версия меньше,значит кто-то уже успел изменить данные. Р>И да, вроде как подобный механизм есть в самих СУБД Multi version concurrency. Это надо по докам смотреть.
В нашей платформе похоже нету такого механизма.
И изменить БД, чтобы добавить это специальное поле, я не могу.
Как быть?
Придется перечитывать запись перед записью и в "лоб" сравнивать старые и новые значения?
Здравствуйте, zelenprog, Вы писали:
Z>Как должна делать программа в таких случаях?
Z>Должна ли программа оповещать пользователя при изменении данных Сущности в БД? Z>Должна ли программа проверять актуальность редактируемых свойств Сущности непосредственно перед записью в БД новых значений этих свойств?
Z>Объясните, пожалуйста.
Это зависит прежде всего от того, для чего пишется программа, какие требования к ней выставляются. Легко представить программы, которые должны вести себя в таких ситуациях по-разному. И даже в по-разному в рамках одной и той же программы, например, в зависимости от типа редактируемой сущности, или от прав пользователя. То же касается и вопроса о том, нужно ли сверять всю таблицу, или отдельные записи (каждую отдельно или группами по неким признакам).
Здравствуйте, zelenprog, Вы писали:
Z>Как быть? Z>Придется перечитывать запись перед записью и в "лоб" сравнивать старые и новые значения?
Да, перечитывать запись придется. Но сравнение можно упростить — вычислять и сравнивать хэш от строки таблицы. Хранить хэши не нужно.
Когда клиент читает данные: вычисляем хэш от строчки в БД и передаем его клиенту.
Когда клиент записывает данные: клиент передает изменения строчки, а также передает имеющийся у него хэш.
Снова вычисляем хэш от имеющейся строчки в БД, и сравниваем с клиентским.
Если хэши совпали, то данные можно записывать. Если не совпали, значит кем-то были изменены.
Р>>версионирование записей БД. специальное поле которое увеличевается на 1 при обновлении. Р>>Если при записи у вас версия меньше,значит кто-то уже успел изменить данные. Р>>И да, вроде как подобный механизм есть в самих СУБД Multi version concurrency. Это надо по докам смотреть.
Z>В нашей платформе похоже нету такого механизма. Z>И изменить БД, чтобы добавить это специальное поле, я не могу.
Z>Как быть? Z>Придется перечитывать запись перед записью и в "лоб" сравнивать старые и новые значения?
если нужно, от требовании исходим
Z>>"и делается запрос актуальных данных из БД" — а запрос каких данных? Всех? S>- А потом вас там публично выпорют как бродяг и отправят в Сибирь убирать снег! S>- Весь? S>- Да S>(Навеяло)
Б>Да, перечитывать запись придется. Но сравнение можно упростить — вычислять и сравнивать хэш от строки таблицы. Хранить хэши не нужно. Б>Когда клиент читает данные: вычисляем хэш от строчки в БД и передаем его клиенту.
Б>Когда клиент записывает данные: клиент передает изменения строчки, а также передает имеющийся у него хэш. Б>Снова вычисляем хэш от имеющейся строчки в БД, и сравниваем с клиентским. Б>Если хэши совпали, то данные можно записывать. Если не совпали, значит кем-то были изменены.
Понятно, спасибо!
А что надо сделать после того как в базу записаны новые данные?
В этот момент у нас есть две записи для этого товара:
— первая запись в "исходной" таблице, которая содержит старые значения реквизитов и хэш
— вторая запись в отредактированной пользователем "пользовательской" таблице на форме, значения реквизитов из этой таблицы (измененные пользователем) были записаны в базу
Ясно, что мы должны обновить запись и в "исходной" таблице.
Как это правильно сделать?
Варианта два:
1) Записать новые реквизиты из записи "пользовательской" таблицы в запись "исходной" таблицы
или
2) Перечитать из БД данные о товаре в "исходную" таблицу. А также в "пользовательскую" таблицу.
Вроде как этот вариант надежнее. Однако, он менее производительный, более трудоемкий.
Как лучше сделать?
Как это обычно делается на практике в реальных хороших программах?
Здравствуйте, zelenprog, Вы писали:
Z>А если серьезно?
Если серьезно, то ровне тем же запросом, каким вы получили данные из БД в "оригинальную" таблицу: