Подтвердить что ты не робот

Передовая практика распространения уникальных нарушений Исключения из пользовательского интерфейса

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

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

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

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

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

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

@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);
    }
}

В представлении "Назад" Bean вы обрабатываете исключения:

@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 - Пусть база данных обнаруживает нарушения ограничений

Преимущество: код котловой плиты. Нет лишних запросов к db. Отсутствие дублирования логики ограничения данных.

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

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;
    }
}
4b9b3361

Ответ 1

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

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

Ответ 2

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

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;
}

Ответ 3

Чтобы избежать шаблона, я рассматриваю DataIntegrityViolationException в ExceptionInfoHandler, обнаруживая вхождения БД в сообщение с первопричиной и преобразовывая его в сообщение i18n через карту. Смотрите код здесь: fooobar.com/info/437105/...