3

(レガシー)JavaアプリとDBを使用していて、MyBatisの複数の列を使用して単一の配列プロパティにマップする必要があります

私のクエリは次のようなものです

select price1, price2, price3, qty1, qty2,qty3 ... from table where ....

対応するpojoは

public class Price{
    double[] prices = new double[3]
    int[] quantity = new int[3]
    public double[] getPrices(){}
    public void setPrices(double[] prices){...}
    public int[] getQty(){...}
    public void setQty(int[] quantities){...}
    ....
}

残念ながら、クエリもJavaオブジェクトも変更できません

ヒントはありますか?

4

3 に答える 3

4

また、BaseTypeHandlerインターフェースを実装することもでき、getNullableResultメソッドでResultSet変数を使用できます。したがって、必要なすべての列を簡単に取得して、それらを配列に配置できます。

于 2012-10-29T16:18:06.043 に答える
2

SqlSession#selectもう1つのオプションは、CreateHandlerを使用することです。これは、クエリから返されるデータを処理するためにメソッドに渡すことができるMyBatisインターフェースです。

これを使用して問題を解決する方法は次のとおりです。

まず、PriceResultHandlerを次のように定義します。

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

public class PriceResultHandler implements ResultHandler {

  // keep a list, assuming you have multiple Price objects to handle
  List<Price> lp = new ArrayList<Price>();

  @Override
  public void handleResult(ResultContext rc) {
    // the result object is map with column names mapped to values like so:
    // {price1=11.32, qty1=15500, price2=2.62, qty2=234, etc.}

    HashMap<?,?> m = (HashMap<?,?>) rc.getResultObject();
    Price p = new Price();
    double[] prices = new double[]{ ((Number)m.get("price1")).doubleValue(),
                                    ((Number)m.get("price2")).doubleValue(),
                                    ((Number)m.get("price3")).doubleValue() };
    int[] qty = new int[]{ ((Number)m.get("qty1")).intValue(),
                           ((Number)m.get("qty2")).intValue(),
                           ((Number)m.get("qty3")).intValue() };
    p.setPrices(prices);
    p.setQty(qty);
    lp.add(p);
  }


  public List<Price> getPrices() {
    return lp;
  }

ResultHandlerがHashMapを受け取る理由は、マッピングを次のように設定するためです。

<select id="getPrices" resultType="hashmap">
  SELECT price1, price2, price3, qty1, qty2, qty3 
  FROM table 
  WHERE ...
</select>

次に、Javaコードで次のように呼び出します。

PriceResultHandler rh = new PriceResultHandler();
session.select("getPrices", rh);
// or
session.select("getPrices", arg, rh);
// where arg is a value to pass to the getPrices query mapping

Price p = rh.getPrices().get(0);

このモデルは、他の回答で示した他のオプションとほぼ同じ量の作業です。MyBatisは、JDBC ResultSetをマップにマッピングするだけで、適切と思われるドメインオブジェクトを作成します。

この方法の注意点の1つは、マップ内の列名の大文字と小文字を区別する問題に対処する必要があることです。MyBatisでこれを制御することはできません。基盤となるJDBCドライバーの動作によって異なります:https ://stackoverflow.com/a/11732674/871012

于 2012-08-07T00:48:17.640 に答える
2

クエリを変更できないと言います。つまり、SQLを変更できないが、MyBatisマッピングを変更できる場合は、MyBatisObjectFactoryを使用することをお勧めします。のサブクラスを定義し、メソッドDefaultObjectFactoryをオーバーライドしますcreate。ObjectFactoryは、MyBatisResultMapで「コンストラクター引数」として指定した引数を受け取ります。

MyBatisマッピングでは、実際にはそうではありませんが、価格フィールドがコンストラクター引数であると指定されるようになりました。これは、生データを独自のハンドラー/ファクトリに渡すための便利な方法です。

<resultMap id="priceResultMap" type="Price">
  <constructor>
    <arg column="price1" javaType="double"/>
    <arg column="price2" javaType="double"/>
    <arg column="price3" javaType="double"/>
    <arg column="qty1" javaType="int"/>
    <arg column="qty2" javaType="int"/>
    <arg column="qty3" javaType="int"/>
  </constructor>
</resultMap>

<select id="getPrice" resultMap="priceResultMap">
  SELECT price1, price2, price3, qty1, qt2, qty3 ...
  FROM table 
  WHERE ...
</select>

これをmybatisconfig.xmlに入れることで、MyBatisのデフォルトのObjectFactoryをオーバーライドします。

<objectFactory type="net.foo.bar.PriceObjectFactory"/>

そして、PriceObjectFactoryは次のようになります。

import org.apache.ibatis.reflection.factory.DefaultObjectFactory;

public class PriceObjectFactory extends DefaultObjectFactory {

  private static final long serialVersionUID = 3627013739044L;

  @Override
  public <T> T create(final Class<T> type, final List<Class<?>> ctorArgTypes,
                      final List<Object> ctorArgs) {
    if (type.equals(Price.class)) {
      return this.<T>createPrice(ctorArgs);
    } else {
      // let MyBatis handle the other object types as it normally does
      return super.<T>create(type, ctorArgTypes, ctorArgs);
    }
  }

  private <T> T createPrice(final List<Object> ctorArgs) {
    final int expSize = 6;
    if (ctorArgs.size() != expSize) {
      throw new IllegalArgumentException("Expected " + expSize +
                                         " ctor args for Price class");
    }

    // construct with no arg ctor
    final Price p = new Price();
    double[] prices = new double[]{ ((Number)ctorArgs.get(0)).doubleValue(), 
                                    ((Number)ctorArgs.get(1)).doubleValue(),
                                    ((Number)ctorArgs.get(2)).doubleValue()};
    int[] qty = new int[]{ ((Number)ctorArgs.get(3)).intValue(),
                           ((Number)ctorArgs.get(4)).intValue(),
                           ((Number)ctorArgs.get(5)).intValue() };
    p.setPrices(prices);
    p.setQty(qty);

    @SuppressWarnings("unchecked")
    final T t = (T) p;
    return t;
  }
}

また、Priceコンストラクターへの追加の引数(idなど)が必要な場合<constructor>は、マッピングのセクションでそれらを指定すると、PriceObjectFactoryにも渡されます。

于 2012-08-05T13:56:47.370 に答える