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 >= #eventFilter.beginDate::timestamp AND ev.occurred_on <= #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我终于让它工作了。我最终不得不更改的唯一一件事是在我的 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
属性也可能取array
和list
之类的值,所以这是为了兼容性。【参考方案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迭代复杂对象参数内的整数列表的主要内容,如果未能解决你的问题,请参考以下文章