Mybatis 中 selectKey的用法

大家好,我们今天来看下mysql中selectKey的用法。

selectKey返回最近一次插入的id

返回自增ID

 
  1. <selectKey resultType="java.lang.Integer" keyProperty="id" order="BEFORE" >//AFTER
  2. SELECT LAST_INSERT_ID()
  3. </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代码:

 
  1. XML代码
  2. <insert id="insert" parameterType="SysUser" >
  3. < selectKey keyProperty="id" order="BEFORE" resultType="java.lang.string">
  4. select uuid()
  5. </selectKey>
  6. insert into sys_user
  7. (id, name, email, phone)
  8. values
  9. (#{id},#{name},#{email},#{phone})
  10. </insert>
 

应用层代码 

 
  1. SysUser user=new SysUser();
  2. user.setId("354646465465465464sdfasdfasdfasdf");
  3. user.setName("测试");
  4. user.setEmail("11@qq.com");
  5. user.setPhome("15866669999");
  6. sysUserDao.insert(user);
 

 数据库结果,我们可以看出,数据库中的id并不是在应用层给赋值的那个id,而是执行select uuid() 的结果作为user的id。

关于生成uuid的方式,BEFORE是先设置完成id之后进行插入操作。

 
  1. <insert id="insert" parameterType="SysUser" >
  2. < selectKey keyProperty="id" order="AFTER" resultType="java.lang.string">
  3. select LAST_INSERT_ID()
  4. </selectKey>
  5. insert into sys_user
  6. (id, name, email, phone)
  7. values
  8. (#{id},#{name},#{email},#{phone})
  9. </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

 

posted @ 2024-01-02 16:25  CharyGao  阅读(687)  评论(0编辑  收藏  举报