Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)

     这个问题困扰我有一段时间了,问题应用的场景是:数据库有三张表,两两之间是互相关联的,关联的ID就是各个Table中的主键ID;涉及多表的关系插入,显然在创建表的时候,表的主键ID不能设置成自增(MySql下,auto_increament)键,而且自增主键在并发的场景下,同一时刻如果产生了两个一样的主键ID,是不是insert的时候服务就要崩掉了,就算在事物控制下也会造成用户数据的丢失,非常不友好。怎么办呢?于是乎,我想到了用MySql的序列sequence,我建立了一个全局序列表--sequence,显然三张表的主键ID全部来自于此序列表,不管你怎么来,执行一次select序列表查询,取出的序列值都是不一样的(并发下未做测试,应该能应对),为此,我给出了下面的Mysql.txt文件作为数据演示的脚本:


DROP TABLE  player;
DROP TABLE user;
DROP TABLE race;


##创建user

CREATE TABLE `user` (
  
`id` int(11) NOT NULL, 
`name` varchar(50) NOT NULL,
 
`age` int(11) DEFAULT NULL,
  
`sex` varchar(4) DEFAULT NULL,
  
`pwd` varchar(50) DEFAULT NULL,
  
`role` int(11) DEFAULT NULL,
  
PRIMARY KEY (`id`)
) 
ENGINE=InnoDB
DEFAULT 
CHARSET=utf8;


##创建player,添加外键key关联
CREATE TABLE `player` (
  
`pID` INT NOT NULL,
`uID` INT NOT NULL,
  
`nationality` VARCHAR(100) NULL,
  
`professional` VARCHAR(100) NULL,
  
PRIMARY KEY (`uID`, `pID`))

ENGINE = InnoDB
DEFAULT 

CHARACTER SET = utf8;

ALTER TABLE player ADD CONSTRAINT uID FOREIGN KEY(uID) REFERENCES `user`(id);

##创建race
CREATE TABLE `race` (
  
`rID` INT NOT NULL ,
  
`pID` INT NOT NULL,
  
`skin` VARCHAR(10) NULL,
  
PRIMARY KEY (`rID`, `pID`))

ENGINE = InnoDB

DEFAULT 
CHARACTER SET = utf8;


##创建序列表
DROP TABLE IF EXISTS sequence;   
CREATE TABLE sequence (   
     name VARCHAR(50) NOT NULL,   
     current_value INT NOT NULL,   
     increment INT NOT NULL DEFAULT 1,   
     PRIMARY KEY (name)   
) ENGINE=InnoDB;   


##取当前序列中的值
DROP FUNCTION IF EXISTS currval;   
DELIMITER $   
CREATE FUNCTION currval (seq_name VARCHAR(50))   
     RETURNS INTEGER  
     LANGUAGE SQL   
     DETERMINISTIC   
     CONTAINS SQL   
     SQL SECURITY DEFINER   
     COMMENT ''  
BEGIN  
     DECLARE value INTEGER;   
     SET value = 0;   
     SELECT current_value INTO value   
          FROM sequence  
          WHERE name = seq_name;   
     RETURN value;   
END  
$   
DELIMITER ;


##取下一个值
DROP FUNCTION IF EXISTS nextval;   
DELIMITER $   
CREATE FUNCTION nextval (seq_name VARCHAR(50))   
     RETURNS INTEGER  
     LANGUAGE SQL   
     DETERMINISTIC   
     CONTAINS SQL   
     SQL SECURITY DEFINER   
     COMMENT ''  
BEGIN  
     UPDATE sequence  
          SET current_value = current_value + increment   
          WHERE name = seq_name;   
     RETURN currval(seq_name);   
END  
$   
DELIMITER ;   
   

#更新当前值
DROP FUNCTION IF EXISTS setval;   
DELIMITER $   
CREATE FUNCTION setval (seq_name VARCHAR(50), value INTEGER)   
     RETURNS INTEGER  
     LANGUAGE SQL   
     DETERMINISTIC   
     CONTAINS SQL   
     SQL SECURITY DEFINER   
     COMMENT ''  
BEGIN  
     UPDATE sequence  
          SET current_value = value   
          WHERE name = seq_name;   
     RETURN currval(seq_name);   
END  
$   
DELIMITER ;   


##创建一个全局对象的序列
INSERT INTO sequence VALUES ('ObjectSeq', 0, 1);
##取一个序列值出来
SELECT NEXTVAL('ObjectSeq');


脚本语句可直接在MySql下批量执行:



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第1张图片

   


       

       有了序列,接着就是在项目中进行演示,到目前为止,我还是没有指出具体的问题究竟是什么,先卖个关子,下面来看一下演示的项目目录树:



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第2张图片



     我们在SqlMapper中,给出我们的全局序列“ObjectSeq”的查询语句(带返回值的)



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第3张图片




    为了快速演示,我只给出这个方法在事物控制下,多次调用时所产生的尴尬问题的分析:



A.



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第4张图片



B.各位看官注意了,马上就要方法调用了,魔术表演即将开始



(1) API接口调用



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第5张图片



(2) Console控制台信息输出



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第6张图片



(3) 如果我们不加事物注解呢?



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第7张图片




再走一遍,我们发现又不一样了(我们要的就是这个效果):



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第8张图片



呵呵呵呵哒,究其原因,还是在事物这块,那就从这里出发,就不信了,mybatis在这个小问题上还能有bug?莫非是......



突然有一天,大神告诉我,应该这样做:



加上一行注解@Options(flushCache=Options.FlushCachePolicy.TRUE)



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第9张图片




然后,然后就有然后了(把之前的事物注解再加回来)



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第10张图片



再看一下,这个“bug”会不会被完美干掉



Mybatis事务下解决多次select语句查询结果一致的问题+案列(获取全局序列ID)_第11张图片



 

     Ok,序列值很连续,完美的解决了我心中遗留的问题。 有些人会问我,如果不用@Options注解,能不能解决这个“bug”呢?

    

       我的答案是:能!首先,你得在每一次调用GetObjectSeq方法拿到序列值之后,对这个序列值进行insert操作入库,这样的话,下一次mybaits在遇到select查询序列值的时候,就不会把它当做是一次缓存操作,而是强制刷新缓冲,重新进行select,为什么呢? 在事物下,mybatis会对select语句的结果添加到缓存,换句话说就是:

      

       如果在同一个事物中,多次对同一个查询sql进行执行的话,mybatis只会查询一次数据库,而后几次的返回结果实际上是从缓冲中取的,因此,我们看到,在没有加@Options注解之前,我们查询了三次序列值,返回的结果“惊人的相似”,然而我们加了@Options注解之后,一切又恢复了正常,究其原因,还是我们对mybatis了解的太少,附上一个mybatis3配置+Java API的资料链接,防止日后我们在mybatis中采坑吧:点击打开链接




    





      





你可能感兴趣的:(mybatis)