MyBatis将多个列映射到数组

时间:2012-08-03 15:13:57

标签: mybatis

我正在使用(旧版)Java应用和数据库,我需要将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对象

任何提示?

3 个答案:

答案 0 :(得分:4)

您也可以实现BaseTypeHandler接口,并且在getNullableResult方法中,ResultSet变量可供您使用。因此,您可以轻松获取所需的所有列并将它们放入阵列。

答案 1 :(得分:2)

您说您无法修改查询 - 如果这意味着您无法更改SQL但您可以更改MyBatis映射,那么我建议您使用MyBatis ObjectFactory。您定义DefaultObjectFactory的子类并覆盖create方法。 ObjectFactory接收您在MyBatis ResultMap中指定为“构造函数args”的参数。

您的MyBatis映射现在将指定price字段是构造函数参数,即使它们不是真的。这只是将原始数据传递给您自己的处理程序/工厂的便捷方式。

<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>

通过将它放在mybatis config.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。

答案 2 :(得分:1)

另一种选择是使用创建一个ResultHandler,这是一个MyBatis接口,您可以将其传递给SqlSession#select方法来处理从查询中返回的数据。

以下是如何使用它来解决您的问题。

首先定义一个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映射到Map中,然后根据需要使用它创建域对象。

此方法的一个警告是您必须处理映射中列名称的区分大小写问题。您无法通过MyBatis控制此操作。它取决于底层JDBC驱动程序的行为:https://stackoverflow.com/a/11732674/871012