Разработка методов, связанных с базой данных, которые лучше возвращать: верно / неверно или затронуты строки?


10

У меня есть несколько методов, которые выполняют изменение данных в базе данных (вставка, обновление и удаление). ORM Я использую возврат строки , затронутые Int значение для этих типов методы. Что я должен вернуть для «моего метода», чтобы указать состояние успеха / неудачи операции?

Рассмотрим код, который возвращает int:

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

Тогда использование:

А.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

Сравните с использованием логического:

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

Тогда использование:

БИ 2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

Какой из них (A или B) лучше? Или плюсы / минусы каждого?


В вашем "А.2". Если затрагиваются нулевые строки, то почему происходит сбой, если нужно затрагивать нулевые строки? Другими словами, если нет ошибки базы данных, почему это сбой?
Джейди

5
Есть ли смысловая разница между «неудачными» и «затронутыми нулевыми строками»? Например, при удалении всех заказов клиента существует разница между «клиент не существует» и «клиент не имеет заказов».
Головоногий

Считаете ли вы удаление с нулевыми строками неожиданным? В таком случае брось.
USR

@ Ариан: Думаю, это реальный вопрос для меня. Я думаю, что выбрал бы B, потому что с A мой код теперь содержит проверку на 0 в некоторых местах и ​​на -1 в других
Hoang Tran

Ответы:


18

Другой вариант - вернуть объект результата вместо базовых типов. Например:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

При этом, если по какой-то причине вам нужно вернуть количество затронутых строк, вы просто можете добавить метод в OperationResult:

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

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


2
+1 «Этот дизайн позволяет вашей системе расти и включать новые функциональные возможности (зная о затронутых строках) без изменения существующего кода». Это правильный подход к данной категории вопросов.
InformedA

Я сделал подобное в моем старом проекте. У меня есть объект запроса и результата. Оба используют Композиции, чтобы включить больше деталей. и у обоих есть основные данные в этом также. В этом случае объект Result имеет код состояния и поле сообщения.
ИнформированоA

Специально для системы, использующей ORM, я бы назвал это лучшей практикой. Рассматриваемый ORM также может включать в себя такие типы объектов результатов!
Брайан С

Просто глядя на пример OP, все, о чем я могу думать, это то, что deleteById в какой-то момент вернет 2 :), так что определенно пользовательский тип, да.
deadsven

Мне нравится этот подход. Я думаю, что это неблокирующее, охватывающее оба моих подхода и модифицируемое / расширяемое (в будущем, если необходимо). Единственный недостаток, о котором я могу подумать, это немного больше кода, особенно когда он находится в середине моего проекта. Я отмечу это как ответ. Спасибо.
Хоанг Тран

12

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

Ни один программист не будет винить вас, потому что он / она должен написать это, чтобы проверить, не получили ли они какие-либо изменения во время операции:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

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

Кстати: если под «отказом» вы подразумеваете синтаксическую ошибку запроса (в этом случае число затронутых строк, очевидно, равно 0), то выбрасывание исключения будет более уместным.


4
-1 из-за причины, которую вы дали. Предоставление дополнительной информации не всегда хорошая идея. Зачастую, лучший дизайн привел бы к тому, чтобы сообщить вызывающему абоненту только то, что ему нужно, и ничего более.
Арсений Мурзенко

1
@MainMa ничто не мешает создавать перегруженные методы. Кроме того, в родных языках (например, в семействе C) число строк может использоваться непосредственно в логике (0 - false, все остальное - true).
PTwr

@PTwr: так, чтобы справиться с тем, что метод возвращает слишком много информации, вы предлагаете создать перегрузку? Это не кажется правильным. Что касается «ноль - это ложь, ненулевое - это истина», это не главное в моем комментарии, и это плохая практика в некоторых языках.
Арсений Мурзенко

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

4
Вы должны всегда возвращать количество затронутых строк здесь !!! Поскольку вы не можете знать, является ли число строк = 0 неудачным или число строк = 3 успешным? Если кто-то хочет вставить 3 строки и вставить только 2, вы вернете true, но это неправильно! И если кто-то хочет, чтобы update t set category = 'default' where category IS NULLдаже 0 затронутых строк были бы успешными, потому что теперь нет элемента без категории, даже если ни одна строка не была затронута!
Falco

10

Я бы не рекомендовал ни одного из них. Вместо этого ничего не возвращайте (void) в случае успеха и генерируйте исключение при ошибке.

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

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

Могут произойти сбои / исключительные ситуации, и тогда вам придется иметь дело с ними. Используете ли вы try / catch для этого или проверяете коды возврата, это вопрос стиля / личных предпочтений. Идеи try / catch заключаются в следующем: отделить нормальный поток от исключительного потока и позволить исключениям пузыриться до уровня, где они могут обрабатываться наиболее подходящим образом. Итак, как уже указывали многие, это зависит от того, является ли провал действительно исключительным или нет.


4
-1 Почему бы вам не вернуть ничего, где вы могли бы вернуть что-то, без негативных побочных эффектов, что обеспечивает дополнительный операционный контекст?
FreeAsInBeer

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

1
Ну, а какие данные на самом деле нужно получить пользователю? Вопрос в том, как указать успех / ошибку. В этом случае достаточно сообщить о неудаче, выдав исключение и ничего не возвращая при успехе. Почему я должен предоставлять больше, чем нужно пользователю?
Проскор

4
@proskor: Исключения для исключительных случаев. «Сбой» в этом сценарии может быть ожидаемым результатом. Конечно, выдвиньте это как потенциальную альтернативу, но здесь недостаточно информации, чтобы дать рекомендацию.
Ник Барнс

1
-1 Исключения не должны быть частью нормального программного потока. Неясно, что означает «сбой» в контексте вашей ошибки, но исключение в контексте вызова базы данных должно происходить из-за исключения, возникающего в базе данных. Влияние на ноль строк не должно быть исключением. Исключенный из-за искаженного запроса, который не может быть проанализирован, ссылается на несуществующую таблицу и т. Д., Будет исключением, потому что ядро ​​базы данных захлебнется и выбросит.

2

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

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

Моя точка зрения такова: какое решение лучше, полностью зависит от ваших требований к данному приложению, и вы знаете эти обстоятельства гораздо лучше, чем мы. Не существует общеотраслевого, безопасного в использовании, наилучшего практического решения, которое не будет уволено из-за него, что лучше в целом ; Вы должны думать об этом сами. И для решения, столь же легко пересмотренного, как и это, вам не нужно тратить столько времени на размышления.


Я согласен, что это во многом зависит от требований приложения. Однако, похоже, что подобная ситуация не очень уникальна, и я не ищу серебряную пулю, а просто опыт других, имеющих дело с той же / подобной вещью (возможно, вопрос немного вводит в заблуждение, мне нравятся предложения, отличные от A / B)
Хоанг Чан

1

Двумя важнейшими принципами разработки программного обеспечения для сопровождения являются KISS и YAGNI .

Он почти никогда не является хорошей идеей , чтобы положить в логике вы не сразу нужно прямо сейчас . Среди многих других людей об этом писал Джефф Этвуд (соучредитель StackExchange) , и, по моему опыту, он и другие сторонники этих концепций совершенно правы.

Любая сложность, которую вы добавляете в программу, платная в течение длительного периода времени. Программа становится более трудной для чтения, более сложной для изменения и более легкой для выявления ошибок. Не попадайтесь в ловушку добавления вещей «на всякий случай». Это ложное чувство безопасности.

Вы редко когда-либо получите правильный код с первого раза. Изменения неизбежны; добавление спекулятивной логики для защитной подготовки к неизвестным будущим непредвиденным обстоятельствам фактически не защитит вас от необходимости рефакторинга кода, когда будущее окажется не таким, как вы ожидали. Поддержание ненужной / условной логики - это скорее проблема сопровождения, чем последующий рефакторинг для добавления недостающей функциональности.

Таким образом, поскольку кажется, что на данный момент все, что нужно вашей программе, это знать, была ли операция успешной или неудачной, предлагаемое вами решение B (верните единственное логическое значение) является правильным подходом. Вы всегда можете выполнить рефакторинг позже, если требование изменится. Это простейшее решение, имеющее наименьшую сложность (KISS) и выполняющее только то, что вам нужно, и ничего более (YAGNI).


-1

Целые строки или статус ошибки

Подумайте о возврате целых строк, по крайней мере, во время выполнения. При вставке в БД вам может понадобиться проверить данные, которые были вставлены - поскольку они часто будут отличаться от того, что вы отправили в БД; К распространенным примерам относятся автоматически сгенерированные идентификаторы строк (которые, скорее всего, понадобятся приложению), значения по умолчанию, определяемые БД, и результаты триггеров, если вы их используете.

С другой стороны, если вам не нужен возвращаемые данные, то вы также не нужны пораженное количество строк, так как это не полезно для результата 0. Если есть ошибки, то вам необходимо вернуть , какой из ошибка произошла, в соответствии с принципами обработки ошибок вашего проекта (исключения, числовые коды ошибок и т. д.); но есть допустимые запросы, которые будут корректно влиять на 0 строк (т.е. «удалить все просроченные ордера», если их на самом деле нет).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.