web-dev-qa-db-ja.com

Spring RowMapperインターフェースはどの程度正確に機能しますか?

私はSpringCore認定の勉強をしていますが、SpringがJDBCクエリをどのように処理するかについて疑問があります。

したがって、取得する予定のデータのタイプに応じて、さまざまな方法でDBテーブルからデータを取得できることがわかっています。

1)単純型(int、long、またはStringとして)のクエリ:jdbcTemplateクラスのqueryForObject()メソッドを使用します、そのようなもの:

String sql = "SELECT count(*) FROM T_REWARD";
int rowsNumber = jdbcTemplate.queryForObject(sql, Integer.class);

したがって、単純なオブジェクトをint値として取得するには、queryForObject()メソッドを使用してsql stattmentと、からの出力で受け取ると予想されるオブジェクトタイプを渡します。方法。

わかりました、これはかなり簡単で、大丈夫だと思います。

2)テーブル行全体マップオブジェクトに入力するようにクエリします。したがって、単一の値が必要ない場合(1つの特定の行の単一の列に含めることができます)テーブルまたは前の例のようなもの)queryForMap(..)およびqueryForList()メソッドを次のように使用できます。

2.1)queryForMap()単一行単一マップオブジェクトに配置されると予想される場合に使用します。各列の値は私の地図の、次のようなもの:

String sql = "select * from T_REWARD where CONFIRMATION_NUMBER = ?";
Map<String, Object> values = jdbcTemplate.queryForMap(sql,confirmation.getConfirmationNumber());

2.2)queryForList():クエリの出力としてより多くの行が必要な場合に使用します。したがって、Mapオブジェクトのリストを取得します。ここで、各Mapオブジェクトはクエリ出力の特定の行を表します。それのようなもの:

String sql = “select * from PERSON”;
return jdbcTemplate.queryForList(sql);

これもかなりはっきりしていると思います。

次に、JdbcTemplateを使用してResultSetドメインオブジェクトにマップできますが、これは私にはそれほど明確ではありません。

ドキュメントを読むと、JdbcTemplateはコールバックアプローチを使用してこれをサポートしていることがわかります。これは正確にはどういう意味ですかコールバックアプローチ

SpringがRowMapperインターフェース for ResultSetの単一行をオブジェクトにマッピングするを提供することを私は知っています:

public interface RowMapper<T> {
    T mapRow(ResultSet rs, int rowNum)
    throws SQLException;
}

queryForObject()メソッドの返されたオブジェクトとして新しいRestaurandRowMapperオブジェクトを使用するこのメソッドによる次の例の構成があります。

public Restaurant findByMerchantNumber(String merchantNumber) {
    String sql = "select MERCHANT_NUMBER, NAME, BENEFIT_PERCENTAGE, BENEFIT_AVAILABILITY_POLICY from T_RESTAURANT where MERCHANT_NUMBER = ?";

    return jdbcTemplate.queryForObject(sql, new RestaurantRowMapper(), merchantNumber);

そしてこれ内部クラス

class RestaurantRowMapper implements RowMapper<Restaurant> {
    public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
        return mapRestaurant(rs);
    }
}

このプライベートメソッドを使用してマッピングを作成します。

private Restaurant mapRestaurant(ResultSet rs) throws SQLException {
    // get the row column data
    String name = rs.getString("NAME");
    String number = rs.getString("MERCHANT_NUMBER");
    Percentage benefitPercentage = Percentage.valueOf(rs.getString("BENEFIT_PERCENTAGE"));
    // map to the object
    Restaurant restaurant = new Restaurant(number, name);
    restaurant.setBenefitPercentage(benefitPercentage);
    restaurant.setBenefitAvailabilityPolicy(mapBenefitAvailabilityPolicy(rs));
    return restaurant;
}

だから私はこれらすべてのものがどのように正確に機能するかを理解するのにいくつかの困難があります。

私の主な疑問は次のとおりです。queryForObject()メソッドを使用して、入力パラメーターとして、出力として期待するオブジェクトのタイプ(たとえば、Intergerまたは長いです)。

テーブルの行全体を表すドメインオブジェクトを取得することを期待している場合(たとえば、レストランテーブルの行がレストランオブジェクト)にマップされている)を使用する必要があると考えていますこのオブジェクト(Restaurantオブジェクトとして)ですが、前の例では、代わりに**行マッパーオブジェクトを使用していますドメインオブジェクト

return jdbcTemplate.queryForObject(sql, new RestaurantRowMapper(), merchantNumber);

この内部クラスには、期待されるドメインオブジェクトを返すmapRow()メソッドのみが含まれます。

class RestaurantRowMapper implements RowMapper<Restaurant> {
    public Restaurant mapRow(ResultSet rs, int i) throws SQLException {
        return mapRestaurant(rs);
    }
}

したがって、Springは自動的にmapRow()メソッドを呼び出してRestaurandドメインオブジェクトを返し、これは自動的にqueryForObject()メソッドなどに置き換えられると思います。しかし、私はそれが正確に機能するかどうかはよくわかりません。

何が足りないのですか?舞台裏で何が起こっているのか説明してもらえますか?

Tnx

7
user4353372

queryForObjectメソッドは次のようになります。

_public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
    List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));
    return DataAccessUtils.requiredSingleResult(results);
}
_

queryForObject-メソッドは、内部的にqueryオブジェクトのメソッドJdbcTemplateを呼び出します。 query-メソッドは次のように定義されます。

_public <T> T query(
        PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
        throws DataAccessException;
_

ご覧のとおり、_ResultSetExtractor<T>_はquery-メソッドに渡され、Springは_RowMapper<T>_をタイプnew RowMapperResultSetExtractor<T>(rowMapper, 1)のオブジェクトに簡単に変換します。 RowMapperResultSetExtractorは、魔法の鍵を握るオブジェクトです。オブジェクトが呼び出されると、次のスニペットに従ってすべての行が繰り返されます。

_public List<T> extractData(ResultSet rs) throws SQLException {
    List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
    int rowNum = 0;
    while (rs.next()) {
        results.add(this.rowMapper.mapRow(rs, rowNum++));
    }
    return results;
}
_

したがって、元のRowMapperは、各行に対して呼び出されるコールバックです。さらに、ここでわかるように、一致するすべての結果に対してRowMapperが呼び出され、作成したRestaurant-オブジェクトが結果リストに追加されます。ただし、oneオブジェクトのみをクエリするため、最後に次のステートメントを使用して単一のRestaurantオブジェクトを返します。

_        return DataAccessUtils.requiredSingleResult(results);
_

したがって、要約すると、JdbcTempalte戦略パターン を実装します(これは テンプレートメソッドパターン に似ています)。また、StrategyインターフェイスRowMapper)を提供することで、JdbcTemplateに手間のかかる作業(例外、接続などの処理)を行わせることができます。 RowMapperは各ヒットをPOJO(Restaurant)としてマップし、すべてのヒットはListに収集されます。次に、メソッドqueryForObjectはそのListから最初の行を取得し、それを呼び出し元に返します。戻り値は、RowMapperのジェネリック型(この場合はRestaurant)に基づいています。

7
wassgren

JdbcTemplate.queryForObjectを使用すると、このような問題が解決されます

public YourPojo getDatabaseDetails(int masterId) {

    sql = "Select * FROM <table_name> where ID=?";
    YourPojo pojo = (YourPojo) jdbcTemplate.queryForObject(sql,
            new Object[] { masterId }, new RowMapper<YourPojo>() {
                @Override
                public <YourPojo> mapRow(ResultSet rs, int rowNum)
                        throws SQLException {
                    YourPojo pojo2 = new YourPojo();
                    pojo2.setAccountId(rs.getString("AccountId"));
                    pojo2.setAccountName(rs.getString("AccountName"));
                    pojo2.setAccountCRN(rs.getString("AccountCRN"));
                    pojo2.setAccountStatus(rs.getString("AccountStatus"));
                    return pojo2;
                }
            });
    return pojo;
}
0