#java #algorithm #design-patterns #jakarta-ee
#java #алгоритм #шаблоны проектирования #джакарта-ee
Вопрос:
У меня есть фрагмент кода из старого проекта.
Логика (на высоком уровне) следующая:
пользователь отправляет серию {id,Xi}
, где id является первичным ключом объекта в базе данных.
Цель состоит в том, чтобы база данных обновлялась, но последовательность Xi
значений всегда была уникальной.
Т.е. Если пользователь отправляет {1,X1}
и в базе данных, которую мы имеем {1,X2},{2,X1}
, ввод должен быть отклонен, иначе мы получим дубликаты, т.Е. {1,X1},{2,X1}
т.Е. у нас X1
дважды в разных строках.
На более низком уровне пользователь отправляет ряд пользовательских объектов, которые инкапсулируют эту информацию.
В настоящее время реализация для этого использует «грубую силу», т.Е. Непрерывные циклы for по вводу и результирующему набору jdbc для обеспечения уникальности.
Мне не нравится этот подход, и, кроме того, в фактической реализации есть незначительные ошибки, но это уже другая история.
Я ищу лучший подход, как с точки зрения кодирования, так и производительности.
Я думал о следующем:
- Создайте a
Set
из списка ввода пользователя. Если размерSet
отличается от list, то ввод пользователя имеет дубликаты.Остановитесь на этом. - Загрузите данные из jdbc.
- Создайте a
HashMap<Long,String>
с вводом пользователя. Ключ является первичным ключом. - Цикл по результирующему набору. Если
HashMap
неcontain
имеет ключа с тем же значением, что и идентификатор строки результирующего набора, добавьте его вHashMap
- В конце получаем
HashMap
значения какList
.Если он содержит дубликаты, отклоните ввод.
Это алгоритм, который я придумал.
Есть ли лучший подход, чем этот? (Я предполагаю, что я не ошибаюсь в самом алгоритме)
Комментарии:
1. Почему бы просто не использовать уникальное ограничение для столбца в базе данных?
2. На данный момент я не могу изменить базу данных. Я могу только изменить старый код
3. Подождите, вы хотите сказать
{1, X2}
, что это строковое значение в одном столбце базы данных?4. Нет, таблица — это таблица с БОЛЬШИМ КОЛИЧЕСТВОМ столбцов (это огромная схема). Это лишь небольшая часть, о которой я говорю, я заинтересован в исправлении для конкретного варианта использования
Ответ №1:
Чисто с точки зрения производительности, почему бы не позволить базе данных выяснить, что есть дубликаты (например {1,X1},{2,X1}
)? Установите уникальное ограничение в таблице, а затем, когда оператор update завершается неудачно, генерируя исключение, перехватите его и разберитесь с тем, что вы хотели бы сделать при этих входных условиях. Вы также можете захотеть запустить это как единую транзакцию, только если вам нужно откатить какие-либо частичные обновления. Конечно, это предполагает, что у вас нет никаких других бизнес-правил, управляющих обновлениями, о которых вы здесь не упоминали.
С вашим алгоритмом вы тратите слишком много времени на перебор HashMap
s и List
s, чтобы удалить дубликаты IMHO.
Комментарии:
1. просто имейте в виду, что база данных не волшебная, у вас может возникнуть серьезная проблема с производительностью, если вы перенесете все такие функции в базу данных.
Ответ №2:
Поскольку вы не можете изменить базу данных, как указано в комментариях. Я бы, вероятно, расширил вашу заданную идею. Создайте a HashMap<Long, String>
и поместите в него все элементы из базы данных, а затем также создайте a HashSet<String>
со всеми значениями из вашей базы данных.
Затем, когда вы просматриваете пользовательский ввод, сверьте ключ с hashmap и посмотрите, совпадают ли значения, если да, то отлично, вам ничего не нужно делать, потому что этот точный ввод уже есть в вашей базе данных.
Если они не совпадают, проверьте значение по сравнению с HashSet, чтобы узнать, существует ли оно уже. Если это так, то у вас есть дубликат.
Должно работать намного лучше, чем цикл.
Редактировать:
Для нескольких обновлений выполните все обновления для HashMap
созданного из вашей базы данных, затем еще раз проверьте Map
установленное значение, чтобы увидеть, отличается ли его размер от набора ключей.
Возможно, есть лучший способ сделать это, но это лучшее, что у меня есть.
Комментарии:
1. Я думаю, что во второй части логики есть небольшая ошибка. Когда выполняется проверка на
HashSet
соответствие, чтобы увидеть, существует ли значение, если я рассматриваю существование как дубликат, я не разрешаю пользователю вставлять перевернутый кортеж. Т.е. в БД{1,X1},{2,X2}
и пользовательский ввод{1,X2},{2,X1}
. Это разрешено, но ваш алгоритм отклонит его. Верно?2. @user384706 Да, это правильно, я не знал, что вы хотите, чтобы несколько обновлений могли обрабатываться одновременно. Позвольте мне посмотреть, смогу ли я заставить это работать.
Ответ №3:
Я бы выбрал решение на стороне базы данных. Предполагая таблицу со столбцами id
и value
, вы должны составить список со всеми «значениями» и использовать следующий SQL:
select count(*) from tbl where value in (:values);
однако привязка :values
параметра к списку значений подходит для вашей среды. (Тривиально при использовании Spring JDBC и базы данных, поддерживающей in
оператор, тем более для меньших настроек. В качестве последнего средства вы можете генерировать SQL динамически.) Вы получите результирующий набор с одной строкой и одним столбцом числового типа. Если значение равно 0, вы можете вставить новые данные; если оно равно 1, сообщите о нарушении ограничений. (Если это что-то еще, у вас есть совершенно новая проблема.)
Если вам нужно проверять каждый элемент в пользовательском вводе, измените запрос на:
select value from tbl where value in (:values)
сохраните результат в наборе (вызываемом, например duplicates
, ), а затем перебирайте элементы пользовательского ввода и проверяйте, находится ли значение текущего элемента duplicates
.
Это должно работать лучше, чем перенос всего набора данных в память.
Комментарии:
1. Здесь есть одна проблема. Пользователю разрешено отправлять данные пакетами. И пользователю разрешено отправлять
{1,X2},{2,X1}
, и в БД это так{1,X1},{2,X2}
. Т.е. отправить обратный кортеж. Таким образом, после обновления данные по-прежнему уникальны. Этот подход не допускает этого, верно?2. @user384706: Нет, не совсем. Вы можете изменить его с помощью транзакций. Запустите транзакцию, выполните обновление, затем проверьте наличие дубликатов, используя что-то вроде
select value, count(*) from tbl where value in (:values) group by value
. Если все значения равны 1, дубликатов введено не было, и вы можете зафиксировать транзакцию. Если какой-либо из них больше 1, соответствующее значение является дубликатом, и вы должны прервать транзакцию. В зависимости от того, насколько подробными должны быть ваши отчеты об ошибках, вы можете либо уменьшить, либо увеличить сложность.