32

JDBI を使用して dropwizard で単純な REST アプリケーションを作成しています。次のステップは、1 対多の関係を持つ新しいリソースを別のリソースと統合することです。これまで、別のテーブルからオブジェクトのリストを保持する単一のオブジェクトを取得するメソッドを DAO で作成する方法を理解できませんでした。

POJO 表現は次のようになります。

ユーザー POJO:

public class User {

    private int id;
    private String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

アカウント POJO:

public class Account {

    private int id;
    private String name;
    private List<User> users;

    public Account(int id, String name, List<User> users) {
        this.id = id;
        this.name = name;
        this.users = users;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}

DAO は次のようになります。

public interface AccountDAO {

    @Mapper(AccountMapper.class)
    @SqlQuery("SELECT Account.id, Account.name, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
    public Account getAccountById(@Bind("id") int id);

}

しかし、メソッドが単一のオブジェクトを戻り値として持つ場合 ( List<Account>ではなくAccount )、Mapper クラスの resultSet の複数の行にアクセスする方法はないようです。私が見つけることができる唯一の解決策はhttps://groups.google.com/d/msg/jdbi/4e4EP-gVwEQ/02CRStgYGtgJで説明されていますが、その解決策は単一のオブジェクトを持つセットのみを返しますが、これはあまり見えませんエレガント。(また、リソース クラスでは適切に使用できません。)

流暢な API でFolder2を使用する方法があるようです。しかし、それを dropwizard と適切に統合する方法がわかりません。dropwizard のドキュメントで推奨されているように、JDBI の SQL オブジェクト API に固執したいと思います。

JDBI で SQL オブジェクト API を使用して 1 対多のマッピングを取得する方法は本当にないのでしょうか? これは、データベースの非常に基本的な使用例であるため、何かが欠けているに違いないと思います。

すべての助けに感謝します、
  ティルマン

4

4 に答える 4

35

わかりました、たくさん検索した後、これに対処する方法が 2 つあります。

最初のオプションは、各列のオブジェクトを取得し、それをリソースの Java コードにマージすることです (つまり、データベースで行うのではなく、コードで結合を行います)。これは次のような結果になります

@GET
@Path("/{accountId}")
public Response getAccount(@PathParam("accountId") Integer accountId) {
    Account account = accountDao.getAccount(accountId);
    account.setUsers(userDao.getUsersForAccount(accountId));
    return Response.ok(account).build();
}

これは小規模な結合操作には適していますが、データベースが行うべきことであるため、私にはあまり洗練されていないように思えます。しかし、私のアプリケーションはかなり小さく、多くのマッパー コードを書きたくなかったので、この方法を取ることにしました。

2 番目のオプションは、結合クエリの結果を取得し、次のようにオブジェクトにマップするマッパーを作成することです。

public class AccountMapper implements ResultSetMapper<Account> {

    private Account account;

    // this mapping method will get called for every row in the result set
    public Account map(int index, ResultSet rs, StatementContext ctx) throws SQLException {

        // for the first row of the result set, we create the wrapper object
        if (index == 0) {
            account = new Account(rs.getInt("id"), rs.getString("name"), new LinkedList<User>());
        }

        // ...and with every line we add one of the joined users
        User user = new User(rs.getInt("u_id"), rs.getString("u_name"));
        if (user.getId() > 0) {
            account.getUsers().add(user);
        }

        return account;
    }
}

DAO インターフェイスには、次のようなメソッドがあります。

public interface AccountDAO {

    @Mapper(AccountMapper.class)
    @SqlQuery("SELECT Account.id, Account.name, User.id as u_id, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
    public List<Account> getAccountById(@Bind("id") int id);

}

注:コレクション以外の戻り値の型を使用すると、抽象的な DAO クラスは静かにコンパイルされますpublic Account getAccountById(...);。ただし、マッパーは、SQL クエリが複数の行を検出した場合でも、1 行の結果セットしか受け取りません。これは、マッパーが喜んで 1 人のユーザーの 1 つのアカウントに変換されます。JDBI は、コレクション以外の戻り値の型を持つLIMIT 1forSELECTクエリを課しているようです。DAO を抽象クラスとして宣言すると、具体的なメソッドを DAO に配置できるため、1 つのオプションは、次のようにパブリック/保護されたメソッドのペアでロジックをラップすることです。

public abstract class AccountDAO {

    @Mapper(AccountMapper.class)
    @SqlQuery("SELECT Account.id, Account.name, User.id as u_id, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
    protected abstract List<Account> _getAccountById(@Bind("id") int id);

    public Account getAccountById(int id) {
        List<Account> accountList = _getAccountById(id);
        if (accountList == null || accountList.size() < 1) {
            // Log it or report error if needed
            return null;
        }
        // The mapper will have given a reference to the same value for every entry in the list
        return accountList.get(accountList.size() - 1);
    }
}

リレーショナル データの操作には通常多くの結合があるため、これはまだ少し面倒で低レベルに思えます。より良い方法や、JDBI が SQL オブジェクト API を使用してこのための抽象操作をサポートすることを望んでいます。

于 2014-07-03T08:33:07.143 に答える
4

1 対多および 1 対 1 の関係を維持するのに非常に役立つ小さなライブラリがあります。また、デフォルトのマッパーにより多くの機能を提供します。

https://github.com/Manikandan-K/jdbi-folder

于 2014-11-20T17:47:04.150 に答える