工作中被同事提出一个bug,跟踪了一下发现这个bug不是每次都能重现,几番周折,发现是我代码本身的写法和mybatis中一个配置凑巧碰到了一块出现的这个问题。

        先看代码的写法:(这中写法就是为了不用每次都创建对象)

        User user = new User();

        user.setId(x);

        user = dao.get(user);

        ...

        user.setId(y);

        user = dao.get(user);

        ...

        再来看一下mybatis配置中的那一项就是localCacheScope。

        先来认识一下localCacheScope是什么:

        MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。


        项目中使用的是默认设置,那就是会话中查询数据共享,项目是一个流程流转的操作,流转到下一步时,除了指定下一步处理人,还需要额外设置五个评委,为方便讲述我暂且把下一步处理人叫做A,发现提交一下步之后,这个处理人有的时候不是A,而是其中一个评委,这个问题原因隐藏比较深,现在一点一点分析:

        在组织参数之前,这六个人每一个人都需要调用同一个dao的方法查询,如果这五个评委中没有A这个人的时候,那这个流程流转不会出现问题,因为这六次查询的参数每次都不一样,当这个A就是其中一个评委的时候,那就出现上面说的问题了(这六次调用dao方法的顺序是先五个评委,最后才是A,其实这个A在五个评委中的排序不是最后一个的时候才会出现,如果是最后一个也不会出现),debug发现的时候会发现,我调试的时候A在评委中排序是第四个,这个五个评委五次查询都会调用dao方法,最后执行A的时候,本应该也调用dao查询,但是发现没有调用,并且A对象中的查询条件属性变了,这个变的值就是最后一个评委对应属性的值,其他的属性是对的。这样让大家看可能不是很明白,下面我个举个具体的例子:

        假设每一个人的对象是User,dao方法是根据id属性当条件查询

第四次查询   评委4(A这个人)   User(id=1,name=null) => 调用dao方法   =>  User(id=1,name=1)


user.setId(2);//根据上面的localCacheScope配置,这个user对象其实已经被缓存了下来,此时user还指向了id为1查询出来的对象,但是这一步已经把id改成2了,这里改的是内存中被缓存下来的对象。


 第五次查询  评委5  User(id=2,name=null)  => 调用dao方法 => User(id=2,name=2)


user.setId(1);//这次参数还是1,由于已经被mybatis缓存了下来,所有不会调用sql查询,直接返回缓存下来对象,也就是上面被修改的对象。


第六次查询  A这个人  User(id=1),name=null  =>  本地缓存机制,不调用dao  => User(id=2,name=1)


所以就导致了最后一次查询是这个结果。


        虽然这次的问题和代码写法有关系,但是这种缓存机制确实也有一定的危险性,最后决定还是把这个localCacheScope改成了STATEMENT