Лучшие практики по распространению уникальных исключений в пользовательском интерфейсе - PullRequest
16 голосов
/ 23 марта 2012

Мы работаем над веб-проектом Java на основе JPA 2, Hibernate, Spring 3 и JSF 2, запущенных в Tomcat 7. Мы используем Oracle 11g в качестве базы данных.

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

Подход 1 - Программно проверить и выдать конкретное исключение

В CountryService.java каждое ограничение Unique будет проверено, и будет выдано соответствующее исключение. Исключения обрабатываются индивидуально в компоненте поддержки.

Преимущество : Легко понять и поддерживать. Возможны конкретные сообщения пользователя.

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

@Service("countryService")
public class CountryServiceImpl implements CountryService {

    @Inject
    private CountryRepository countryRepository;

    @Override
    public Country saveCountry(Country country) throws NameUniqueViolationException,  IsoCodeUniqueViolationException, UrlUniqueViolationException {
        if (!isUniqueNameInDatabase(country)) {
            throw new NameUniqueViolationException();
        }
        if (!isUniqueUrl(country)) {
            throw new UrlUniqueViolationException();
        }
        if (!isUniqueIsoCodeInDatabase(country)) {
            throw new IsoCodeUniqueViolationException();
        }
        return countryRepository.save(country);
    }
}

В базовом компоненте View вы обрабатываете исключения:

@Component
@Scope(value = "view")
public class CountryBean {

    private Country country;

    @Inject
    private CountryService countryService;

    public void saveCountryAction() {
        try {
            countryService.saveCountry(country);
        } catch (NameUniqueViolationException e) {
            FacesContext.getCurrentInstance().addMessage("name", new FacesMessage("A country with the same name already exists."));
        } catch (IsoCodeUniqueViolationException e) {
            FacesContext.getCurrentInstance().addMessage("isocode", new FacesMessage("A country with the same isocode already exists."));
        } catch (UrlUniqueViolationException e) {
            FacesContext.getCurrentInstance().addMessage("url", new FacesMessage("A country with the same url already exists."));
        } catch (DataIntegrityViolationException e) {
             // update: in case of concurrent modfications. should not happen often
             FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("The country could not be saved."));
        }
    }
}

Подход 2. Позволяет базе данных обнаруживать нарушения ограничений

Преимущество : Код котельной пластины отсутствует. Нет ненужных запросов к БД. Нет дублирования логики ограничения данных.

Недостаток : Зависимости от имен ограничений в БД, поэтому создание схемы через спящий режим невозможно. Механизм, необходимый для привязки сообщений к компонентам ввода (например, для выделения).

public class DataIntegrityViolationExceptionsAdvice {
    public void afterThrowing(DataIntegrityViolationException ex) throws DataIntegrityViolationException {

        // extract the affected database constraint name:
        String constraintName = null;
        if ((ex.getCause() != null) && (ex.getCause() instanceof ConstraintViolationException)) {
            constraintName = ((ConstraintViolationException) ex.getCause()).getConstraintName();
        }

        // create a detailed message from the constraint name if possible
        String message = ConstraintMsgKeyMappingResolver.map(constraintName);
        if (message != null) {
            throw new DetailedConstraintViolationException(message, ex);
        }
        throw ex;
    }
}

Ответы [ 3 ]

12 голосов
/ 23 марта 2012

Подход 1 не будет работать в параллельном сценарии!- Всегда есть изменение, что кто-то другой вставляет новую запись в базу данных после того, как вы проверили, но до того, как вы добавили свою запись в базе данных.(за исключением того, что вы используете сериализуемый уровень изоляции, но это маловероятно)

Таким образом, вы должны обработать исключения нарушения ограничений БД.Но я рекомендую перехватить исключение базы данных, которое указывает на уникальное нарушение, и добавить более полное значение, как вы предложили в подходе 1.

9 голосов
/ 23 марта 2012

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

try {
    return countryRepository.save(country);
}
catch (DataIntegrityViolationException ex) {
    if (!isUniqueNameInDatabase(country)) {
        throw new NameUniqueViolationException();
    }
    if (!isUniqueUrl(country)) {
        throw new UrlUniqueViolationException();
    }
    if (!isUniqueIsoCodeInDatabase(country)) {
        throw new IsoCodeUniqueViolationException();
    }
    throw ex;
}
0 голосов
/ 24 февраля 2017

Чтобы избежать шаблонов, я рассматриваю DataIntegrityViolationException в ExceptionInfoHandler, нахожу вхождения ограничений DB в сообщении первопричины и преобразовываю его в сообщение i18n через карту. Смотрите код здесь: https://stackoverflow.com/a/42422568/548473

...