Mybatis foreach迭代复杂对象参数内的整数列表

Posted

技术标签:

【中文标题】Mybatis foreach迭代复杂对象参数内的整数列表【英文标题】:Mybatis foreach iteration over list of integers within a complex object parameter 【发布时间】:2014-12-24 19:44:56 【问题描述】:

我在 Play Framework 2.3.6 Java 项目中使用 MyBatis 3.2.8。几天来,我一直在努力迭代在复杂对象参数中传递给 MyBatis 映射器的整数列表。这是我的设置:

我在 EventFilter.java 中有一个名为 EventFilter 的类:

public class EventFilter 
private String beginDate;
private String endDate;
private List<Integer> closestCountry;
private List<Integer> territorialWaterStatus;
private List<Integer> vesselCountry;
private String closestCountryInClause;
private String territorialWaterStatusInClause;
private String vesselCountryInClause;

public EventFilter()  

public EventFilter(JsonNode jsonNode)
    this.beginDate = jsonNode.get("beginDate").asText();
    this.endDate = jsonNode.get("endDate").asText();
    this.closestCountry = JsonHelper.arrayNodeToIntegerList((ArrayNode) jsonNode.get("closestCountry"));
    this.territorialWaterStatus = JsonHelper.arrayNodeToIntegerList((ArrayNode) jsonNode.get("territorialWaterStatus"));
    this.vesselCountry = JsonHelper.arrayNodeToIntegerList((ArrayNode) jsonNode.get("vesselCountry"));


public String getBeginDate() 
    return beginDate;


public void setBeginDate(String beginDate) 
    this.beginDate = beginDate;


public String getEndDate() 
    return endDate;


public void setEndDate(String endDate) 
    this.endDate = endDate;


public List<Integer> getTerritorialWaterStatus() 
    if(this.territorialWaterStatus.size() > 0) 
        return territorialWaterStatus;
     else 
        return null;
    


public void setTerritorialWaterStatus(List<Integer> territorialWaterStatus) 
    this.territorialWaterStatus = territorialWaterStatus;


public List<Integer> getClosestCountry() 
    if(this.closestCountry.size() > 0) 
        return closestCountry;
     else 
        return null;
    


public void setClosestCountry(List<Integer> closestCountry) 
    this.closestCountry = closestCountry;


public List<Integer> getVesselCountry() 
    if(this.vesselCountry.size() > 0) 
        return vesselCountry;
     else 
        return null;
    


public void setVesselCountry(List<Integer> vesselCountry) 
    this.vesselCountry = vesselCountry;

这在我的 mybatis 配置文件中被引用为类型别名:

<configuration>

<typeAliases>
    <typeAlias type="models.Event" alias="Event"/>
    <typeAlias type="models.EventFilter" alias="EventFilter"/>
    <typeAlias type="models.Country" alias="Country"/>
</typeAliases>

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="org.postgresql.Driver"/>
            <property name="url" value="jdbc:postgresql://localhost:5432/mpmap"/>
            <property name="username" value="postgres"/>
            <property name="password" value="dbpw"/>
        </dataSource>
    </environment>
</environments>

<mappers>
    <mapper resource="EventMapper.xml"/>
</mappers>
</configuration>

我有一个将 EventFilter 对象作为参数的映射器。然后它应该检查是否设置了 beginDate、endDate、closestCountry、vesselCountry 和 TerritoryWaterStatus。如果是,则将它们用于 WHERE 子句:

<select id="getEventsWithFilter" resultType="Event" resultMap="EventResult">
    SELECT ev.id, to_char(ev.occurred_on, 'YYYY-MM-DD') AS occurred_on_date,
        to_char(ev.occurred_on, 'HH24:MI:SS') AS occurred_on_time,
        ST_X(ev.location) AS longitude, ST_Y(ev.location) AS latitude,
        COALESCE(co01.name, 'Unspecified') AS closest_country,
        COALESCE(co02.name, 'Unspecified') AS territorial_water_status,
        COALESCE(co03.name, 'Unspecified') AS vessel_flag_country
    FROM event AS ev
    LEFT JOIN country AS co01
        ON co01.cow_id = ev.location_closest_country_id
    LEFT JOIN country AS co02
        ON co02.cow_id = ev.location_water_status_country_id
    LEFT JOIN country AS co03
        ON co03.cow_id = ev.vessel_flag_country_id
    <where>
        <if test="#eventFilter.beginDate != null and #eventFilter.endDate != null">
            ev.occurred_on &gt;= #eventFilter.beginDate::timestamp AND ev.occurred_on &lt;= #eventFilter.endDate::timestamp
        </if>
        <if test="#eventFilter.closestCountry != null">
            AND ev.location_closest_country_id IN
            <foreach item="id" index="index" collection="#eventFilter.closestCountry" open="(" separator="," close=")">
                #id
            </foreach>
        </if>
        <if test="#eventFilter.territorialWaterStatus != null">
            AND ev.location_water_status_country_id IN
            <foreach item="id" index="index" collection="#eventFilter.territorialWaterStatus" open="(" separator="," close=")">
                #id
            </foreach>
        </if>
        <if test="#eventFilter.vesselCountry != null">
            AND ev.vessel_flag_country_id IN
            <foreach item="id" index="index" collection="#eventFilter.vesselCountry" open="(" separator="," close=")">
                #id
            </foreach>
        </if>
    </where>
    ORDER BY ev.occurred_on ASC;
</select>

我将映射器链接到如下界面:

    public List<Event> getEventsWithFilter(@Param("eventFilter") EventFilter eventFilter);

我使用 MybatisMapper 辅助类调用它,生成我的会话如下:

public static List<Event> getEvents(EventFilter eventFilter) 
    MybatisMapper mapper = new MybatisMapper();
    SqlSession session = mapper.getSession();
    EventMapper eventMapper = session.getMapper(EventMapper.class);

    List<Event> events;

    List<Integer> li = eventFilter.getClosestCountry();

    try 
        events = eventMapper.getEventsWithFilter(eventFilter);
     finally 
        session.close();
    

    return events;

问题:

beginDate 和 endDate 本身完全可以正常工作。但是整数列表存在以下问题:

    检查列表是否为空的 if 语句似乎越来越 被忽略,或者当它应该为假时评估为真。 整数列表似乎作为 null 传递到 IN 子句中。 如果我注释掉整数列表 IN 子句,并且只需要 beginDate 和 endDate,它工作得很好。但是,如果我留下整数 列出 IN 子句,查询不会失败,但它返回一个空 设置,好像在说“WHERE column IN ()”。

这是执行映射器/查询时 Play 和 Mybatis 打印的控制台日志,以及打印其内容的 EventFilter。它们有点长,所以我把它们放在 pastebin 里:

当我为三个整数列表中的每一个传递三个国家时:http://pastebin.com/aWcXyikh 当我只传递 beginDate 和 endDate 时:http://pastebin.com/CeCv256g

这比我想要的要长一点,但在此先感谢您的帮助或建议。

【问题讨论】:

另外,当我在映射器中注释掉最后三个 if 语句和迭代时,这里是调试日志:pastebin.com/6feWzd0D 由于我的防火墙限制,我无法看到您的日志,但可能是填充 EventFilter(猜测您的 MVC 框架)的人没有正确填充它,因为您使用的是 List而不是原始数据类型的数组? 嗯。好的,所以使用整数数组而不是整数列表?种种迹象表明,mybatis foreach 似乎对列表和地图友好,但我会尝试一下。 我的建议是确保在调用 myBatis 映射器之前填充数据,即在 getEvents 中,在怀疑 myBatis 之前检查是否首先填充了 Integer 列表。同样,您可能已经这样做了,它会显示在您的日志中,但我看不到它。如果存在,请尝试使用原始数据类型 int 的数组。 啊哈,是的,我已经这样做了,并且在将 eventfilter 对象传递给映射器之前填充了列表。我会尝试将其更改为原始类型,但我希望尽可能保持原样 【参考方案1】:

我终于让它工作了。我最终不得不更改的唯一一件事是在我的 XML 映射器中处理参数列表,而不需要周围的花括号。

所以而不是:

 <if test="#eventFilter.closestCountry != null">
        AND ev.location_closest_country_id IN
        <foreach item="id" index="index" collection="#eventFilter.closestCountry" open="(" separator="," close=")">
            #id
        </foreach>
    </if>

应该是:

<if test="eventFilter.closestCountry != null">
            AND ev.location_closest_country_id IN
            <foreach item="id" index="index" collection="eventFilter.closestCountry" open="(" separator="," close=")">
                #id
            </foreach>
        </if>

这很奇怪,因为在传递的对象中寻址字符串可以使用任何一种方法 - 即 #eventFilter.beginDate 与 eventFilter.beginDate 相同。

一个非常微妙的区别,但我希望它可以帮助某人在未来节省一些时间。

【讨论】:

我怀疑缺少的原因是collection属性也可能取arraylist之类的值,所以这是为了兼容性。【参考方案2】:

试试这个

<if test="eventFilter.closestCountry != null">
        AND ev.location_closest_country_id IN
        <foreach item="id" index="index" collection="eventFilter.closestCountry" open="(" separator="," close=")">
            $id
        </foreach>
    </if>

【讨论】:

【参考方案3】:

我正在使用 MyBatis v. 3.4.6。

我的情况:

@Mapper
public interface CorsoMapper 
    List<CorsoEntity> elenco(Filtro filtro);



public class Filtro implements Serializable 
    private static final long serialVersionUID = -8357822764110641974L;

    private String codice;
    private List<String> codici;

    public String getCodice() 
        return codice;
    

    public void setCodice(String codice) 
        this.codice = codice;
    

    public List<String> getCodici() 
        return codici;
    

    public void setCodici(List<String> codici) 
        this.codici = codici;
    

SQL:

<select id="elencoCorsi" parameterType="it.blabla.Filtro" resultMap="corsoMap">
    AND CODICI IN
    <foreach item="c" collection="codici" open="(" separator="," close=")">
       #c
    </foreach>
</select>

它可以在不使用“索引”属性的情况下工作。

【讨论】:

以上是关于Mybatis foreach迭代复杂对象参数内的整数列表的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis参数传入集合之foreach动态sql

MyBatis参数传入集合之foreach动态sql

mybatis 传入string参数,parametertype怎样写

mybatis <foreach>标签问题

mybatis批量操作(foreach)

MyBatis传入参数为集合 list 数组 map写法