网店版重生系列:多数据源单sqlMapClient导致NullPointerException问题跟踪

从前面的《网店版重生系列:都是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实例即可;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值