Mybatis 中 selectKey的用法
大家好,我们今天来看下mysql中selectKey的用法。
selectKey返回最近一次插入的id
返回自增ID
-
<selectKey resultType="java.lang.Integer" keyProperty="id" order="BEFORE" >//AFTER
-
SELECT LAST_INSERT_ID()
-
</selectKey>
项目中出现的问题:主子表入库时,子表需要主表中的id,当selectKey的order参数设置为BEFORE,获取到的id为0,导致向子表插入数据时出现主键重复的问题。解决方法将BEFORE改为AFTER
先看下selectKey的解释:
主要原因是因为BEFORE是先查最近插入一条的id,在进行插入,把BEFORE改为AFTER就可以解决这个问题。下面我们讨论一下为什么
我们来复现下这个问题
重启项目后第一次操作时总是会出现主键重复的问题,但是从第二操作就会神奇的成功
主表数据中最新的一条数据是applyId为210的数据,主键为自增
第一次进行数据入库
主表插入数据之后,根据SELECT LAST_INSERT_ID() 返回的applyId为0,正常来讲应该返回插入之后的主键211
图一:
由于子表插入失败,事务回滚主表中最新的一条数据还是主键为210的数据
图二:
第二次进行数据入库操作;操作子表插入
主表插入数据之后,根据SELECT LAST_INSERT_ID() 返回的applyId为211,正常来讲应该返回插入之后的主键212,图二中可以看到主表中并没有主键为211的数据。由此可见返回的并非插入到数据库中最近插入数据的主键,不管事务有没有提交成功,但是主键自增过,进行insert操作过,返回的就是最近执行insert中的主键。
图三:
数据库中的数据:我们发现主表中的id是212,但是子表中插入的主键是211
图五:
为什么会出现这种情况,原因是因为mysql 的select LAST_INSERT_ID()语句。
在一个新的statement 中 执行 select LAST_INSERT_ID() 返回为0,但是执行过一次insert语句后,再次执行select LAST_INSERT_ID() 返回的是最新一次插入数据的id。
那么order属性设置为BEFORE这个就是先执行LAST_INSERT_ID()再去操作插入语句,而AFTER正好相反。
如果数据库中id为自增的方式,将selectKey的order参数置为AFTER才会返回正确的结果
2. 关于生成UUid 返回生成uuid
xml代码:
-
XML代码
-
<insert id="insert" parameterType="SysUser" >
-
< selectKey keyProperty="id" order="BEFORE" resultType="java.lang.string">
-
select uuid()
-
</selectKey>
-
insert into sys_user
-
(id, name, email, phone)
-
values
-
(#{id},#{name},#{email},#{phone})
-
</insert>
应用层代码
-
SysUser user=new SysUser();
-
user.setId("354646465465465464sdfasdfasdfasdf");
-
user.setName("测试");
-
user.setEmail("11@qq.com");
-
user.setPhome("15866669999");
-
sysUserDao.insert(user);
数据库结果,我们可以看出,数据库中的id并不是在应用层给赋值的那个id,而是执行select uuid() 的结果作为user的id。
关于生成uuid的方式,BEFORE是先设置完成id之后进行插入操作。
-
<insert id="insert" parameterType="SysUser" >
-
< selectKey keyProperty="id" order="AFTER" resultType="java.lang.string">
-
select LAST_INSERT_ID()
-
</selectKey>
-
insert into sys_user
-
(id, name, email, phone)
-
values
-
(#{id},#{name},#{email},#{phone})
-
</insert>
可以看到数据库中插入的id就是自己设置的id但是执行SELECT LAST _INSERT_ID()返回的始终都是0。
以上两种情况可以看出,BEFORE适合使用在设置UUID的情况,AFTER适合使用在返回自增id的情况
两者搭配的语句也不同,BEFORE===SELECT UUID() ,AFTER===SELECT LAST_INSERT_ID();
Mybatis 中 selectKey的用法_mybatis selectkey-CSDN博客
1、在xml文件中应用useGeneratedKeys和keyProperty
在xml文件中,insert标签属性中,添加useGeneratedKeys和keyProperty,类似如下:
<insert id="insert" parameterType="com.***.Attachment" useGeneratedKeys="true" keyProperty="attachment.id" keyColumn="id">
insert into b_attachment
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="attachment.id != -1">
id,
</if>
<if test="attachment.fileName != null and attachment.fileName !=''">
file_name,
</if>
<if test="attachment.remarks != null and attachment.remarks !=''">
remarks,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="attachment.id != -1">
#{attachment.id,jdbcType=BIGINT},
</if>
<if test="attachment.fileName != null and attachment.fileName !=''">
#{attachment.fileName,jdbcType=VARCHAR},
</if>
<if test="attachment.remarks != null and attachment.remarks !=''">
#{attachment.remarks,jdbcType=VARCHAR},
</if>
</trim>
</insert>
通过这种方式插入的值,经常会返回1,原因是因为他这里的意思是返回当前影响的行数,不能准确返回你新插入的id值,有时候返回的结果是准确的。
2、在xml文件中应用selectKey
在xml文件中,insert标签属性中,添加selectKey标签属性,各个属性值含义:resultType:查询结果的类型;keyProperty:把查询的值赋给谁;order:在插入前还是后执行,id在insert语句插入之后才会生成id,所以要在插入之后执行,所以此处order=after。类似如下:
<insert id="insert" parameterType="com.***.Attachment">
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="attachment.id">
select LAST_INSERT_ID()
</selectKey>
insert into b_attachment
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="attachment.id != -1">
id,
</if>
<if test="attachment.fileName != null and attachment.fileName !=''">
file_name,
</if>
<if test="attachment.remarks != null and attachment.remarks !=''">
remarks,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="attachment.id != -1">
#{attachment.id,jdbcType=BIGINT},
</if>
<if test="attachment.fileName != null and attachment.fileName !=''">
#{attachment.fileName,jdbcType=VARCHAR},
</if>
<if test="attachment.remarks != null and attachment.remarks !=''">
#{attachment.remarks,jdbcType=VARCHAR},
</if>
</trim>
</insert>
注意,keyProperty属性的值,一定为对应对象的主键id,否则获取不到返回的主键id。
3、注解方式
在mapper层用注解的方式,用@SelectKey注解,里面属性含义:resultType:查询结果的类型,keyProperty:把查询的值赋给谁; statement:查找最后一个插入的id; keyColumn:查询的是哪一列; before:是否在插入之前执行, id在insert语句插入之后才会生成id,所以要在插入之后执行,所以此处before=false。
@Insert(insert into b_attachment values(#{id},#{fileName},#{remarks})
@SelectKey(statement = "select last_insert_id() from dual", before = false, resultType = Interger.class, keyColumn = "id", keyProperty = "attachment.id")
int add(@Param("attachment") Attachment attachment);
其中,“select last_insert_id()”这条语句,它是配合插入语句一块只用的,在insert语句执行成功后可以返回新增数据的id。
4、使用方式
在service层,类似如下:
Mybatis之执行插入语句后返回主键id的三种方式_mybatis返回主键id-CSDN博客
起因
有些时候,当我们将数据传输对象保存到数据库时还有后续操作,所以我们需要获取数据传输层对象的ID用于后续操作。
解决方法
只需要在 xml 配置文件中操作标签后面加上
useGeneratedKeys="true" keyProperty="主键标识"
这两个属性即可
例如
<!-- 3.int addEmp(Emp emp); -->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="empId">
insert into t_emp
values (null, #{empName}, #{age}, #{gender}, null)
</insert>
mysql 自增id和UUID做主键性能分析,及最优方案
1.为什么会想到用uuid做主键
(1).其实在innodb存储引擎下,自增长的id做主键性能已经达到了最佳。不论是存储和读取速度都是最快的,而且占的存储空间也是最小。
(2).但是在我们实际到项目中会碰到问题,历史数据表的主键id会与数据表的id重复,两张自增id做主键的表合并时,id一定会有冲突,但如果各自的id还关联了其他表,这就很不好操作。
(3).如果使用UUID,生成的ID不仅是表独立的,而且是库独立的。对以后的数据操作很有好处,可以说一劳永逸。
2.UUID优缺点
缺点: 1. 影响插入速度, 并且造成硬盘使用率低
2. uuid之间比较大小相对于自增id比较大小要慢很多, 影响查询速度。
3. uuid占空间大, 如果你建的索引越多, 影响越严重
优点:出现数据拆分、合并存储的时候,能达到全局的唯一性
3.最优方案
(1).InnoDB引擎表是基于B+树的索引组织表。
(2).B+树:B+树是为磁盘或其他直接存取辅助设备而设计的一种平衡查找树,在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶节点中,各叶节点指针进行连接。
(3).InnoDB主索引:叶节点包含了完整的数据记录。这种索引叫做聚集索引。InnoDB 的索引能提供一种非常快速的主键查找性能。不过,它的辅助索引也会包含主键列,所以,如果主键定义的比较大,其他索引也将很大。如果想在表上定义 、很多索引,则争取尽量把主键定义得小一些。InnoDB 不会压缩索引
(4).聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
综合上述可得:
(1).如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的。为了存储和查询性能应该使用自增长id做主键。
(2).对于InnoDB的主索引,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力,此时不适合使用UUID做物理主键,可以把它作为逻辑主键,物理主键依然使用自增ID。为了全局的唯一性,应该用uuid做索引关联其他表或做外键。
4.如果非要使用uuid做主键,下面是小建议:
如果是主从即M-S模式,最好是不使用mysql自带函数uuid来生成唯一主键,因为主表生成的uuid要再关联从表时,需要再去数据库查出这个uuid,需要多进行一次数据库交互,而且在这个时间差里面主表很有可能还有数据生成,这样就很容易导致关联的uuid出错。如果真要使用uuid,可以在Java中生成后,直接存储到DB里,这时主从的uuid就是一样的了!
http://blog.csdn.net/XDSXHDYY/article/details/78994045