您的位置:首页 > 其它

Ibatis缓存select字段名引起的问题

2009-07-02 20:38 246 查看
开发中遇到一个问题,网上有人描述问题,但是没有找到答案,自己看了ibatis源码才明白过来
执行SQL:

<select id="searchOrderListConut" resultClass="int">
SELECT   count(<isEqual property="useTicketTb" compareValue="true">  distinct  </isEqual> o.id)
    FROM    et_orders o
<isEqual property="useTicketTb" compareValue="true" prepend=",">
         tickets ticket
</isEqual>
   .................这里省略
</ select>


抛出如下异常:

com.taobao.et.biz.dal.exception.DAOException: org.springframework.jdbc.BadSqlGrammarException: SqlMapClient operation; bad SQL grammar []; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:   
--- The error occurred in com/taobao/et/biz/dal/ticket/dataobject/order_sqlmap_mapping.xml.  
--- The error occurred while applying a result map.  
--- Check the orders.searchOrderListConut-AutoResultMap.  
--- Check the result mapping for the 'COUNT(O.ID)' property.  
--- Cause: java.sql.SQLException: 列名无效




先描述一下场景:
订单和机票表是一对多的关系,这条语句是根据机票表中的一些条件和订单中的条件进行分页查询的语句,这里是统计总数。
由于机票表很少查询,所以DBA要求没有查询机票表的时候就不要关联机票表,并不需要distinct关键字。于是就出现了下面这条动态SQL语句,先不管这条语句是否变态,我说说明的问题是,这条语句在上线运行是会出问题的(单元测试不会有任何问题),为什么呢?答案源自于sqlmap的自身的一个处理方式。

sqlmap在执行这条SQL的时候会根据orders.searchOrderListConut这个key来查找select返回的列表字段,如果没有找到,就会分析这条SQL,然后使用orders.searchOrderListConut做为key,selelect中返回的字段列表作为value缓存起来备用(问题就是在这里),以后每次查询这条searchOrderListConut语句的时候,就会直接从缓存中拿出返回字段。假设这条语句先执行了select count(o.id) from et_orders o.....,那么这里缓存起来的字段就是count(o.id),而第二次查询时假设查询了机票表中的信息,那么就需要时select count(distinct o.id) from et_orders o..... ,这时,ibatis使用缓存起来的count(o.id) 作为字段名来取,当然就会取不到值,从而抛出一个异常。


ibaits中缓存列名的地方在这里:

com.ibatis.sqlmap.engine.mapping.result .AutoResultMap 
  public synchronized Object[] getResults(StatementScope statementScope, ResultSet rs)
      throws SQLException {
    if (allowRemapping || getResultMappings() == null) {
      initialize(rs);
    }
    return super.getResults(statementScope, rs);
  }



知道了这个原因,那么问题就可以解决了。既然是根据select 的id缓存了字段名,那么就可以从这里下手了,先不管方法优劣,解决办法如下:
1:既然缓存了,那么第一个方法就是去掉这个缓存。(这个能解决问题,但是不好,又麻烦,又影响性能),之所以单元测试没有发现这个问题,就是因为,单元测试是跑一次就停下来了,再跑第二次,所以这个缓存相当于没有。
2:给这个动态变化的字段增加一个别名,例如
SELECT count(<isEqual property="useTicketTb" compareValue="true"> distinct </isEqual> o.id) num
这样缓存中就会留下num别名作为字段名,至于前面如何变化,ibatis不管
3:自然就是SQL语句拆分,将要关联机票表和不关联机票的SQL语句分开,就不会存在这个问题了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: