免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3859 | 回复: 0
打印 上一主题 下一主题

Spring集成ibatis问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-01 11:39 |只看该作者 |倒序浏览

在使用Spring+Ibatis集成的时候,使用如下方式配置复杂类型的时候,总是报异常


resultMap class="order" id="result">

       result property="orderid" column="OrderId"/>

       result property="customer" column="Customer"/>

       result property="orderLines" select="getOrderLinesByOrder" column="OrderId"/>

     resultMap>
异常如下:


log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).

log4j:WARN Please initialize the log4j system properly.

Exception in thread "main" org.springframework.jdbc.UncategorizedSQLException: SqlMapClient operation; uncategorized SQLException for SQL []; SQL state [null]; error code [0];   

--- The error occurred in ch10/SpringAndIbatisOneToMany/Ibatis.xml.  

--- The error occurred while applying a result map.  

--- Check the result.  

--- Check the result mapping for the 'orderLines' property.  

--- Cause: java.lang.NullPointerException; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:   

--- The error occurred in ch10/SpringAndIbatisOneToMany/Ibatis.xml.  

--- The error occurred while applying a result map.  

--- Check the result.  

--- Check the result mapping for the 'orderLines' property.  

--- Cause: java.lang.NullPointerException

Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:   

--- The error occurred in ch10/SpringAndIbatisOneToMany/Ibatis.xml.  

--- The error occurred while applying a result map.  

--- Check the result.  

--- Check the result mapping for the 'orderLines' property.  

--- Cause: java.lang.NullPointerException

    at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWithCallback(GeneralStatement.java:188)

    at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryForObject(GeneralStatement.java:104)

    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:566)

    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:541)

    at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForObject(SqlMapSessionImpl.java:106)

    at org.springframework.orm.ibatis.SqlMapClientTemplate$1.doInSqlMapClient(SqlMapClientTemplate.java:243)

    at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:193)

    at org.springframework.orm.ibatis.SqlMapClientTemplate.queryForObject(SqlMapClientTemplate.java:241)

    at ch10.SpringAndIbatisOneToMany.TestDAO.getOrderById(TestDAO.java:11)

    at ch10.SpringAndIbatisOneToMany.Test.main(Test.java:22)

Caused by: java.lang.NullPointerException

    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.endTransaction(SqlMapExecutorDelegate.java:782)

    at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.endTransaction(SqlMapSessionImpl.java:176)

    at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.endTransaction(SqlMapClientImpl.java:154)

    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.autoEndTransaction(SqlMapExecutorDelegate.java:883)

    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExecutorDelegate.java:622)

    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExecutorDelegate.java:589)

    at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForList(SqlMapSessionImpl.java:118)

    at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.queryForList(SqlMapClientImpl.java:95)

    at com.ibatis.sqlmap.engine.mapping.result.loader.ResultLoader.getResult(ResultLoader.java:72)

    at com.ibatis.sqlmap.engine.mapping.result.loader.ResultLoader.loadResult(ResultLoader.java:59)

    at com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.getNestedSelectMappingValue(BasicResultMap.java:502)

    at com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.getResults(BasicResultMap.java:340)

    at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlExecutor.java:381)

    at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleMultipleResults(SqlExecutor.java:301)

    at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java:190)

    at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteQuery(GeneralStatement.java:205)

    at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWithCallback(GeneralStatement.java:173)

    ... 9 more

先说解决方式,主要是在Spring配置文件中,一定要为SqlMapClientFactoryBean配置DataSource属性,不能光为DAO配置dataSoure
bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  !-- 此处应注入ibatis配置文件,而非sqlMap文件,否则会出现“there is no statement.....异常” -->
  property name="configLocation">
     value>ch10/SpringAndIbatisOneToMany/sqlMapConfig.xml/value>
  /property>
  property name="dataSource">
   ref bean="dataSource"/>
/property>
/bean>
bean id="testDAO" class="ch10.SpringAndIbatisOneToMany.TestDAO">
  property name="dataSource">
   ref bean="dataSource"/>
   /property>
  property name="sqlMapClient">
    ref bean="sqlMapClient"/>
  /property>
/bean>

源码分析如下:
     经代码跟踪发现问题发生在类org.springframework.orm.ibatis.SqlMapClientTemplate中的:   
     
  public Object execute(SqlMapClientCallback action) throws DataAccessException {
  Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");
   
  SqlMapSession session = this.sqlMapClient.openSession();
  Connection ibatisCon = null;
  try {
  if (logger.isDebugEnabled()) {
  logger.debug("Opened SqlMapSession [" + this.sqlMapClient + "] for iBATIS operation");
  }
  Connection springCon = null;
  try {
                  ibatisCon = session.getCurrentConnection();
  if (ibatisCon == null) {
  springCon = DataSourceUtils.getConnection(getDataSource());
  session.setUserConnection(springCon);
  if (logger.isDebugEnabled()) {
  logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
  }
  }
  else {
  if (logger.isDebugEnabled()) {
  logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
  }
  }
                  return action.doInSqlMapClient(session);
  }
  catch (SQLException ex) {
  throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);
  }
  finally {
  DataSourceUtils.releaseConnection(springCon, getDataSource());
  }
  }
  finally {
  // Only close SqlMapSession if we know we've actually opened it
  // at the present level.
  if (ibatisCon == null) {
  session.close();
  }
  }
  }
   
  在这段代码里,spring从SqlMapSession得openSession()方法中得到了当前的session,   设定了   
  它的连接,   ibatis就用这个session得到了test与testDetail的内容,但在得到gossip的内容时,   
  ibatis使用了如下的方法得到session:   
  类com.ibatis.sqlmap.engine.impl.SqlMapClientImpl中:   
  
      public Object queryForObject(String id, Object paramObject) throws SQLException {
          return getLocalSqlMapSession().queryForObject(id, paramObject);
      }
   
      protected SqlMapSessionImpl getLocalSqlMapSession() {
          SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();
          if (sqlMapSession == null || sqlMapSession.isClosed()) {
              sqlMapSession = new SqlMapSessionImpl(this);
              localSqlMapSession.set(sqlMapSession);
          }
          return sqlMapSession;
      }
        
  在这个方法中可以看出,对于SqlMapClientImpl来说,session应当会被保存在localSqlMapSession中,      
  在使用时再得到.   问题出现了,   spring也是从这个类得到session的,   但在得到session方法:   
   
public SqlMapSession openSession() {
          SqlMapSessionImpl sqlMapSession = new SqlMapSessionImpl(this);
          sqlMapSession.open();
          return sqlMapSession;
      }
  中,   这个session根本就没有保存进localSqlMapSession,   于是在得到gossip的内容时,   使用的是一个   
  新的session.   而这个新的session没有调用过setUserConnection,   其对应的transaction也就是初始值null,   
  而且ibatis在后面的   
  类com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate中   
        
      public Object queryForObject(SessionScope session, String id, Object paramObject, Object resultObject) throws SQLException {
          Object object = null;
   
          MappedStatement ms = getMappedStatement(id);
          Transaction trans = getTransaction(session);
          boolean autoStart = trans == null;
   
          try {
              trans = autoStartTransaction(session, autoStart, trans);
   
              RequestScope request = popRequest(session, ms);
              try {
                  object = ms.executeQueryForObject(request, trans, paramObject, resultObject);
              } finally {
                  pushRequest(request);
              }
   
              autoCommitTransaction(session, autoStart);
          } finally {
              autoEndTransaction(session, autoStart);
          }
   
          return object;
      }
   
      protected Transaction autoStartTransaction(SessionScope session, boolean autoStart, Transaction trans) throws SQLException {
          Transaction transaction = trans;
          if (autoStart) {
              session.getSqlMapTxMgr().startTransaction();
              transaction = getTransaction(session);
          }
          return transaction;
      }
  根本没有检查transaction是不是null,   于是就有上面的NullPointerException了.   
  于是将spring方法的代码改为:   
   
public Object execute(SqlMapClientCallback action) throws DataAccessException {
  Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");
   
  SqlMapSession session = this.sqlMapClient.openSession();
  Connection ibatisCon = null;
  try {
  if (logger.isDebugEnabled()) {
  logger.debug("Opened SqlMapSession [" + this.sqlMapClient + "] for iBATIS operation");
  }
  Connection springCon = null;
  try {
  ibatisCon = this.sqlMapClient.getCurrentConnection();
  if (ibatisCon == null) {
  springCon = DataSourceUtils.getConnection(getDataSource());
                      this.sqlMapClient.setUserConnection(springCon);
  if (logger.isDebugEnabled()) {
  logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
  }
  }
  else {
  if (logger.isDebugEnabled()) {
  logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
  }
  }
  return action.doInSqlMapClient(this.sqlMapClient);
  }
  catch (SQLException ex) {
  throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);
  }
  finally {
  DataSourceUtils.releaseConnection(springCon, getDataSource());
  }
  }
  finally {
  // Only close SqlMapSession if we know we've actually opened it
  // at the present level.
  if (ibatisCon == null) {
  // session.close();
  }
  }
  }
  一切就好了.   

        ibatis的openSession在上一版本时,每次打开的都是同一session,   因为它原来的代码在每次打开前会去localSqlMapSession中查找,如果没有才会打开个新的,而且会在返回前放入localSqlMapSession中,这被认为是一个BUG,所以在这个版本上改为返回一个新的session.而在这个session关闭时,才会把这个session返回给sessionPool中.   
          而在spring中,如楼上我的贴子所说,新打开的session没有进入localSqlMapSession,在打开一对多的表的子表时,   于是从sessionPool中得到一个没有被spring设置过setUserConnection的session,从而Transaction   trans   =   getTransaction(session);这里没有得到缺省的事务,那么   trans   =   autoStartTransaction(session,   autoStart,   trans);代码里就会去找这个session的txManager来启动一个事务,但txManager又是null,于是就会发出nullpoint异常了.   
          这时关键就是session的txManager是什么时候设置的.   我们可以看一下spring的SqlMapClientFactoryBean的代码就会发现,只有设置了它的dataSource属性时,才会去设置session的txManager,   所以这里就可以看出在一对多表时,SqlMapClientFactoryBean的dataSource属性一定要设置,   这样能保证在一对多时才不会出问题.


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/55983/showart_2042837.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP