2

Dropwizard で JDBI を使用していますが、流暢なクエリに問題があります。次のようなAPIエンドポイントがあります。

public Response getAccount(@QueryParam("nickname") String nickname, @QueryParam("email") String email);

次の 3 つのクエリを記述しないようにする必要があります。

@SqlQuery(  " SELECT * FROM ACCOUNT WHERE EMAIL = :email "  )

public Account getAccountByEmail(@Bind("email") String email);


@SqlQuery( " SELECT * FROM ACCOUNT WHERE NICKNAME = :nickname " )

public Account getAccountByNickname(@Bind("nickname") String nickname);


@SqlQuery( " SELECT * FROM ACCOUNT WHERE NICKNAME = :nickname AND EMAIL = :email "  )

public Account getAccount(@Bind("nickname") String nickname, @Bind("email") String email);

これは、メソッド getAccount の実装に 3 つの if チェックがあることも意味します... ニックネームあり、電子メールなし、電子メールあり、ニックネームなし、電子メールあり、ニックネームで、3 つのクエリのどれを実行するかを決定します。別のルックアップ パラメーター (accountId など) を追加すると、実行するクエリを決定するために 6 つのクエリ (可能性ごとに 1 つ) と 6 つの if ステートメントが必要になります。

JDBIでこれを回避する簡単な方法はありますか? @Define の可能性を調べましたが、これには SQL インジェクションのリスクがあります。電子メールとニックネームはどちらも文字列です。

理想的には、クエリは 1 つだけ必要です。

@SqlQuery( " SELECT * FROM ACCOUNT WHERE NICKNAME = :nickname AND EMAIL = :email "  )

public Account getAccount(@Bind("nickname") String nickname, @Bind("email") String email);

ニックネームが null の場合、ニックネームの要件は無視され、メールからクエリが送信されます。このようなことは可能ですか?

編集/更新:

コードを少しクリーンアップするために、これを実行しました。

    @SqlQuery(" SELECT * FROM ACCOUNT WHERE <query> ")
    public Account getAccount(@Bind("id") Long id, @Bind("nickname") String nickname, @Bind("email") String email, @Define("query") String query);

@Define を使用するように JDBI ダオを更新しました。これにより、6 つのクエリ (クエリ フィールドが 3 つあるため、ケースごとに 1 つのクエリ) と、6 つの if ステートメントを 1 つのクエリと 3 つの if ステートメントに減らすことができます。

    Account account = null;
    StringBuilder query = new StringBuilder();

    if(accountId != null) {
        query.append(" ID = :id ");
    }
    if(!Strings.isNullOrEmpty(nickname)) {
        if(query.length() > 0) query.append(" AND ");
        query.append(" NICKNAME = :nickname ");
    }
    else if(!Strings.isNullOrEmpty(email)) {
        if(query.length() > 0) query.append(" AND ");
        query.append(" EMAIL = :email ");
    }

    account = accountDAO.getAccount(accountId, nickname, email, query.toString());

クエリロジックをDAOに保持したいので、defineを使用する必要はありません。このようなことがまったく可能であれば、それは素晴らしいことです:

SELECT * FROM ACCOUNT WHERE @IfNotNull(id = :id) 
AND @IfNotNull(email = :email) AND @IfNotNull(nickname = :nickname)
4

1 に答える 1

1

これは、Java コードで sql を構築する代わりに、単一のクエリでも実現できます。少しハッキーですが。

select * from account where
( COALESCE(:email, NULL) IS NULL OR email = :email) and
( COALESCE(:nickname, NULL) IS NULL OR nickname = :nickname) ;
于 2015-02-06T10:00:20.307 に答える