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

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

 

 

 
  1. DROP TABLE player;

  2. DROP TABLE user;

  3. DROP TABLE race;

  4.  
  5.  
  6. ##创建user

  7.  
  8. CREATE TABLE `user` (

  9.  
  10. `id` int(11) NOT NULL,

  11. `name` varchar(50) NOT NULL,

  12.  
  13. `age` int(11) DEFAULT NULL,

  14.  
  15. `sex` varchar(4) DEFAULT NULL,

  16.  
  17. `pwd` varchar(50) DEFAULT NULL,

  18.  
  19. `role` int(11) DEFAULT NULL,

  20.  
  21. PRIMARY KEY (`id`)

  22. )

  23. ENGINE=InnoDB

  24. DEFAULT

  25. CHARSET=utf8;

  26.  
  27.  
  28. ##创建player,添加外键key关联

  29. CREATE TABLE `player` (

  30.  
  31. `pID` INT NOT NULL,

  32. `uID` INT NOT NULL,

  33.  
  34. `nationality` VARCHAR(100) NULL,

  35.  
  36. `professional` VARCHAR(100) NULL,

  37.  
  38. PRIMARY KEY (`uID`, `pID`))

  39.  
  40. ENGINE = InnoDB

  41. DEFAULT

  42.  
  43. CHARACTER SET = utf8;

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

  46.  
  47. ##创建race

  48. CREATE TABLE `race` (

  49.  
  50. `rID` INT NOT NULL ,

  51.  
  52. `pID` INT NOT NULL,

  53.  
  54. `skin` VARCHAR(10) NULL,

  55.  
  56. PRIMARY KEY (`rID`, `pID`))

  57.  
  58. ENGINE = InnoDB

  59.  
  60. DEFAULT

  61. CHARACTER SET = utf8;

  62.  
  63.  
  64. ##创建序列表

  65. DROP TABLE IF EXISTS sequence;

  66. CREATE TABLE sequence (

  67. name VARCHAR(50) NOT NULL,

  68. current_value INT NOT NULL,

  69. increment INT NOT NULL DEFAULT 1,

  70. PRIMARY KEY (name)

  71. ) ENGINE=InnoDB;

  72.  
  73.  
  74. ##取当前序列中的值

  75. DROP FUNCTION IF EXISTS currval;

  76. DELIMITER $

  77. CREATE FUNCTION currval (seq_name VARCHAR(50))

  78. RETURNS INTEGER

  79. LANGUAGE SQL

  80. DETERMINISTIC

  81. CONTAINS SQL

  82. SQL SECURITY DEFINER

  83. COMMENT ''

  84. BEGIN

  85. DECLARE value INTEGER;

  86. SET value = 0;

  87. SELECT current_value INTO value

  88. FROM sequence

  89. WHERE name = seq_name;

  90. RETURN value;

  91. END

  92. $

  93. DELIMITER ;

  94.  
  95.  
  96. ##取下一个值

  97. DROP FUNCTION IF EXISTS nextval;

  98. DELIMITER $

  99. CREATE FUNCTION nextval (seq_name VARCHAR(50))

  100. RETURNS INTEGER

  101. LANGUAGE SQL

  102. DETERMINISTIC

  103. CONTAINS SQL

  104. SQL SECURITY DEFINER

  105. COMMENT ''

  106. BEGIN

  107. UPDATE sequence

  108. SET current_value = current_value + increment

  109. WHERE name = seq_name;

  110. RETURN currval(seq_name);

  111. END

  112. $

  113. DELIMITER ;

  114.  
  115.  
  116. #更新当前值

  117. DROP FUNCTION IF EXISTS setval;

  118. DELIMITER $

  119. CREATE FUNCTION setval (seq_name VARCHAR(50), value INTEGER)

  120. RETURNS INTEGER

  121. LANGUAGE SQL

  122. DETERMINISTIC

  123. CONTAINS SQL

  124. SQL SECURITY DEFINER

  125. COMMENT ''

  126. BEGIN

  127. UPDATE sequence

  128. SET current_value = value

  129. WHERE name = seq_name;

  130. RETURN currval(seq_name);

  131. END

  132. $

  133. DELIMITER ;

  134.  
  135.  
  136. ##创建一个全局对象的序列

  137. INSERT INTO sequence VALUES ('ObjectSeq', 0, 1);

  138. ##取一个序列值出来

  139. SELECT NEXTVAL('ObjectSeq');


 

 

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

 

 

   

 

       

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

 

 

 

 

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

 

 

 

 

 

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

 

 

A.

 

 

 

 

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

 

 

(1) API接口调用

 

 

 

 

(2) Console控制台信息输出

 

 

 

 

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

 

 

 

 

 

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

 

 

 

 

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

 

 

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

 

 

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

 

 

 

 

 

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

 

 

 

 

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

 

 

 

 

 

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

    

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

      

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值