Java 如何在iBATIS2.3.4中有效地映射复杂集合属性

Java 如何在iBATIS2.3.4中有效地映射复杂集合属性,java,collections,ibatis,Java,Collections,Ibatis,我有一个域对象,它表示数据库表之间的1:n关系 public class ObservationWithData { private Observation observation; private Map<Integer,ElementValue> elementValues; // accessor methods ... } 我有一个连接两个表的选择查询。我想使用resultMap根据datetimeId和stationId将ElementValues与观测

我有一个域对象,它表示数据库表之间的1:n关系

public class ObservationWithData {
   private Observation observation;
   private Map<Integer,ElementValue> elementValues;
   // accessor methods
... }
我有一个连接两个表的选择查询。我想使用resultMap根据datetimeId和stationId将ElementValues与观测值分组,如本例(非工作)所示

<resultMap id="observationWithDataMap" class="ObservationWithData" 
           groupBy="observation.datetimeId,observation.stationId">
  <result property="observation" resultMap="ObservationSql.observationMap" />
  <result property="elementValues" resultMap="ElementSql.elementValueMap"/>
</resultMap>

这有两个问题。首先,groupBy标记不允许嵌套语法,第二个iBATIS要求grouped属性是Java Collections API的一部分,而Map不符合该标准

我可以通过添加datetimeId和stationId访问器来解决第一个问题,第二个问题可以通过创建一个要写入的集合来解决,然后在数据提取后将所有项目添加到地图中

<resultMap id="observationWithDataMap" class="ObservationWithData" 
           groupBy="datetimeId,stationId">
  <result property="stationId" column="station_id" />
  <result property="datetimeId" column="datetime_id" />
  <result property="observation" resultMap="ObservationSql.observationMap" />
  <result property="elementValueCollection" resultMap="ElementSql.elementValueMap"/>
</resultMap>

public class ObservationWithData {
   private Observation observation;
   private Map<Integer,ElementValue> elementValues;
   // this collection is used for database retrieval only; do not add to it
   private Collection<ElementValue> elementValueCollection;

   public void setStationId(int id) { }
   public int getStationId() {
   return observation==null?0:observation.getStationId();
   }
   public void setDatetimeId(int id) { }
   public int getDatetimeId() {
   return observation==null?0:observation.getDatetimeId();
   }

   public Map<Integer,ElementValue> getElementValues() {
   if (elementValues.size()==0 && elementValueCollection.size()>0) {
       for (ElementValue val : elementValueCollection) {
           elementValues.put(val.getElementId(), val);
       }
       elementValueCollection.clear();
   }
   return elementValues;
   }

   public Collection<ElementValue> getElementValueCollection() {
   if (elementValueCollection.size()==0 && elementValues.size()>0) {
       elementValueCollection.addAll(elementValues.values());
       elementValues.clear();
   }
   return elementValueCollection;
   }
   ... }

公共类观测数据{
私人观察;
私有地图元素值;
//此集合仅用于数据库检索;请勿添加到其中
私人收藏元素价值收藏;
public void setStationId(int id){}
public int getStationId(){
返回观察==null?0:observation.getStationId();
}
public void setDatetimeId(int id){}
public int getDatetimeId(){
返回观察==null?0:observation.getDatetimeId();
}
公共映射getElementValues(){
if(elementValues.size()==0&&elementValueCollection.size()>0){
for(ElementValue val:elementValueCollection){
elementValues.put(val.getElementId(),val);
}
elementValueCollection.clear();
}
返回元素值;
}
公共集合getElementValueCollection(){
if(elementValueCollection.size()==0&&elementValues.size()>0){
elementValueCollection.addAll(elementValues.values());
elementValues.clear();
}
返回元素valuecollection;
}
... }
不过,我对这个解决方案不太满意,因为现在有很多公共方法,我不希望人们在代码中使用它们。id设置器是noop,因为这些id是在观察中设置的。我可以让ObservationWithData扩展Observation,但我设计这个类是为了让每个有效Java支持组合而不是继承。我也可以以不同的方式同步映射和集合,但我的想法是让iBATIS填充集合,然后在访问映射时,我将所有值移动到其中,只保留一个引用

有人能推荐一个更优雅的解决方案吗

编辑:

我最终提取了一个服务层来处理这个问题。服务层通过DAO进行两次数据库调用,而不是一次;第一个检索所有观察值,第二个检索所有ElementValues。它使用这些集合中的数据对象构造ObservationWithData对象。它还限制请求,因为这是一个大数据集


这有点笨重和低效,因为我正在“手动”构建对象。然而,由于这是“隐藏”在服务层中的,我觉得它对API用户的干扰较小,他们可以使用一个整洁的域对象。

您是否尝试过编写DAO,以便这是一个两阶段的过程,首先使用数据对象获取观测值,然后使用ElementValue填充它们?它不会对大量的行进行缩放,但是根据您需要获取的内容,现在可能会使它的外部更干净一些


另一种选择是从数据库获取数据,方法是将结果映射到带有getter和setter的包私有对象,然后将它们作为不带setter的不同类返回给调用方。但我不确定这是否有帮助,我认为这可能会给解决方案增加更多混乱。

我们使用ibatis的groupBy属性映射了复杂的集合属性,如下所示-

<resultMap id="actionResultsMap" class="Content" groupBy="objectID">
        <result property="objectID" column="object_id"/>
        <result property="bvReferenceNo" column="reference_no"/>
        .......
        <result property="countryId" resultMap="MyActions.countryMap"/>
</resultMap>

<resultMap id="countryMap" class="java.lang.Integer">
  <result property="value" column="country"/>
</resultMap>

<select id="myActions" resultMap="actionResultsMap"
        parameterClass="java.util.HashMap">
  SELECT 
    object_id, 
    reference_no, 
    ........
    country
   from sysObject so left outer join countryTable ct on ct.object_id = so.object_id
</select>

.......
挑选
对象id,
参考编号:,
........
国家
从sysObject so left外部连接countryTable ct on ct.object\u id=so.object\u id

这里的countryId属于
列表类型

谢谢,Rolf。我确实做了一些类似于你第一个建议的事情。我已经编辑了我的帖子来提供细节。您的第二个解决方案也很有趣,但我同意这更难维护。我还考虑将域对象作为接口,并使用上面描述的更复杂的ObservationWithData对象作为实现。
<resultMap id="actionResultsMap" class="Content" groupBy="objectID">
        <result property="objectID" column="object_id"/>
        <result property="bvReferenceNo" column="reference_no"/>
        .......
        <result property="countryId" resultMap="MyActions.countryMap"/>
</resultMap>

<resultMap id="countryMap" class="java.lang.Integer">
  <result property="value" column="country"/>
</resultMap>

<select id="myActions" resultMap="actionResultsMap"
        parameterClass="java.util.HashMap">
  SELECT 
    object_id, 
    reference_no, 
    ........
    country
   from sysObject so left outer join countryTable ct on ct.object_id = so.object_id
</select>