网店版重生系列:多数据源单sqlMapClient导致NullPointerException问题跟踪
2009-06-20 17:00
453 查看
从前面的《网店版重生系列:都是Spring配置中自动注入惹的祸》中我们可以看出一些有关datasource、sqlMapClientTemplate、sqlMapClient的相关配置信息;整体而言,采取的方式为:
其一,单实例sqlMapClient,只配置configLocation属性,不配置datasource;
其二,sqlMapClientTemplate中配置动态datasource、sqlMapClient,然后在Dao中注入sqlMapClientTemplate;
其三,由于获取用户与Mysql数据库之间的对应关系是保存在Oracle主库中,所以不适用动态数据源,而是直接通过配置sqlMapClient、JNDI datasource来完成注入;
在这种配置方式下,应用初期偶尔会出现报NullPointerException异常信息,堆栈信息如下:
开始的时候,我们还以为是Mysql数据库首次连接出现Time Out所致,但细想下不对啊!如果是这样堆栈信息中应该会包括TimeOut相关的信息才对啊,但是没有这个信息,再看看堆栈信息,是从SqlMapSessionImpl.setUserConnection中抛出来的,跟踪下源码,问题很快就被定位了,原因是SqlMapSession被关闭所致;来看看最为关键的这段代码先:
跟踪代码时发现,其实这段代码被调用了多次,第一次执行时没有任何问题,第二次执行时也没问题,异常信息其实是第三次执行时抛出,直接原因是此时SqlMapSession的实例已被close;根本原因呢还是因为单sqlMapClient实例所致!
在分布式改造过程中,为了避免对用户、Mysql分库信息对应关系的强耦合,没有采取固定的hash取模或者是一致hash算法,而是将用户、Mysql分库信息进行了持久化存储,保存于主库Oracle中,然后再配合Cache优化性能;这样就出现了一种应用场景:
当我们在通过Ibatis在Mysql库中执行SQL语句时,因为要通过datasource获取connection,所以可能需要先访问Oracle主库以便获取当前用户所对应的Mysql数据库信息,而这两者的SQL操作都是通过Ibatis进行的,所以我们上面提到的那段代码就会被多次执行了,而每次SQL执行完毕之后都会到finally部分调用session.close();session的实例是和当前线程绑定的,所以其实那几次代码段的执行都是使用的同一个session;
问题根源都找到了,解决也很简单:针对JNDI datasource、动态datasource区分不同的sqlMapClient,也即配置两个单独的sqlMapClient实例即可;
其一,单实例sqlMapClient,只配置configLocation属性,不配置datasource;
其二,sqlMapClientTemplate中配置动态datasource、sqlMapClient,然后在Dao中注入sqlMapClientTemplate;
其三,由于获取用户与Mysql数据库之间的对应关系是保存在Oracle主库中,所以不适用动态数据源,而是直接通过配置sqlMapClient、JNDI datasource来完成注入;
在这种配置方式下,应用初期偶尔会出现报NullPointerException异常信息,堆栈信息如下:
java.lang.NullPointerException at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.setUserConnection(SqlMapSessionImpl.java:149) at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:179) at com.alisoft.eshop.ddb.dao.support.DdbSqlMapClientTemplate.execute(DdbSqlMapClientTemplate.java:86) at org.springframework.orm.ibatis.SqlMapClientTemplate.delete(SqlMapClientTemplate.java:396) at com.alisoft.eshop.ddb.dao.support.DdbSqlMapClientTemplate.delete(DdbSqlMapClientTemplate.java:53) at com.alisoft.c2c.biz.dal.dao.ibatis.IbatisMigrateDataDao.deleteDBFromMysql(IbatisMigrateDataDao.java:21) at com.alisoft.c2c.biz.manager.impl.MigrationDataImpl.doDeleteFromMysql(MigrationDataImpl.java:170) at com.alisoft.c2c.biz.manager.impl.MigrationDataImpl.migrationDate(MigrationDataImpl.java:69)
开始的时候,我们还以为是Mysql数据库首次连接出现Time Out所致,但细想下不对啊!如果是这样堆栈信息中应该会包括TimeOut相关的信息才对啊,但是没有这个信息,再看看堆栈信息,是从SqlMapSessionImpl.setUserConnection中抛出来的,跟踪下源码,问题很快就被定位了,原因是SqlMapSession被关闭所致;来看看最为关键的这段代码先:
SqlMapSession session = this.sqlMapClient.openSession(); Connection ibatisCon = null; try { if (logger.isDebugEnabled()) { logger.debug("Opened SqlMapSession [" + session + "] 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(); } }
跟踪代码时发现,其实这段代码被调用了多次,第一次执行时没有任何问题,第二次执行时也没问题,异常信息其实是第三次执行时抛出,直接原因是此时SqlMapSession的实例已被close;根本原因呢还是因为单sqlMapClient实例所致!
在分布式改造过程中,为了避免对用户、Mysql分库信息对应关系的强耦合,没有采取固定的hash取模或者是一致hash算法,而是将用户、Mysql分库信息进行了持久化存储,保存于主库Oracle中,然后再配合Cache优化性能;这样就出现了一种应用场景:
当我们在通过Ibatis在Mysql库中执行SQL语句时,因为要通过datasource获取connection,所以可能需要先访问Oracle主库以便获取当前用户所对应的Mysql数据库信息,而这两者的SQL操作都是通过Ibatis进行的,所以我们上面提到的那段代码就会被多次执行了,而每次SQL执行完毕之后都会到finally部分调用session.close();session的实例是和当前线程绑定的,所以其实那几次代码段的执行都是使用的同一个session;
问题根源都找到了,解决也很简单:针对JNDI datasource、动态datasource区分不同的sqlMapClient,也即配置两个单独的sqlMapClient实例即可;
相关文章推荐
- Storm OutputCollector并发问题导致NullPointerException的解决
- Storm OutputCollector并发问题导致NullPointerException的解决
- 关于在win本地远程hadoop集群开发导致的NullPointerException问题
- 照相机拍照NullPointerException 问题
- Android兼容性问题 -- RelativeLayout.mesure()出现NullPointerException异常
- 异常:SqlMapClient operation; uncategorized SQLException for SQL []; SQL state [null]; error code [0];
- hive分区导致FAILED: Hive Internal Error: java.lang.NullPointerException(null)
- 艰辛的aptana插件安装之路(续)--成功解决myeclipse不能启动和报java.lang.NullPointerException的问题
- 重新打开Eclipse出现java.lang.NullPointerException的问题解决
- java中关于nullpointer Exception的问题
- java.lang.NullPointerException--jvm堆栈异常信息丢失问题
- PhoneGap2.7.0调用拍照和相册时出现onActivityResult NullPointerException问题
- Javadoc 执行后报告java.lang.NullPointerException问题求解
- Java Web项目Server窗口NullPointerException问题
- java.lang.NullPointerException 空指针异常问题
- Android 2.3下重写PopupWindow的NullPointerException问题
- web连接数据库时,报空指针java.lang.NullPointerException问题--可能的解决方式
- Nokia平台游戏退出之后出现nullPointerException问题
- Hibernate中遇到的 NullPointerException问题和IllegalArgumentException
- 导致java.lang.NullPointerException异常的一种情况