web-dev-qa-db-ja.com

UIに固有の違反例外を伝播するベストプラクティス

私たちはTomcat7で実行されているJPA2、Hibernate、Spring 3、JSF2に基づくJava Webプロジェクトに取り組んでいます。データベースとしてOracle11gを使用しています。

現在、UIへのユーザーフレンドリーなメッセージとしてデータベース制約違反を設定する方法について議論を行っています。多かれ少なかれ2つの方法がありますが、どちらも実際には満足のいくものではありません。誰かがアドバイスをしてもらえますか?

アプローチ1-プログラムで検証し、特定の例外をスローします

CountryService.Javaでは、各一意の制約が検証され、対応する例外がスローされます。例外は、バッキングBeanで個別に処理されます。

利点:理解と保守が簡単です。特定のユーザーメッセージが可能です。

デメリット:ニースメッセージを表示するためのコードがたくさんあります。基本的に、すべての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への不要なクエリはありません。データ制約ロジックの重複はありません。

デメリット: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;
    }
}
18
fischermatte

アプローチ1は、並行シナリオでは機能しません。 -チェックした後、データベースレコードを追加する前に、他の誰かが新しいデータベースレコードを挿入するという変更が常にあります。 (シリアル化可能な分離レベルを使用する場合を除きますが、これはほとんどありません)

したがって、DB制約違反の例外を処理する必要があります。ただし、固有の違反を示すデータベース例外をキャッチし、アプローチ1で提案したように、より意味のあるものをスローすることをお勧めします。

12
Ralph

これもオプションであり、完全に保存できない場合にのみ詳細な例外のチェックを行うため、コストが低くなる可能性があります。

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

ボイラープレートを回避するために、DataIntegrityViolationExceptionExceptionInfoHandlerを処理し、根本原因メッセージでDB制約の発生を見つけて、マップを介してi18nメッセージに変換します。ここのコードを参照してください: https://stackoverflow.com/a/42422568/54847

0
Grigory Kislin