MyBatis主键生成策略中useGeneratedKeys和<selectKey>的区别

 更新时间:2025年01月22日 10:36:15   作者:山高自有客行路  
本文主要介绍了MyBatis主键生成策略中useGeneratedKeys和<selectKey>的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

useGeneratedKeys

机制与原理

  • JDBC特性useGeneratedKeys利用了JDBC的getGeneratedKeys()方法,该方法是在执行INSERT语句后获取由数据库自动生成的主键值。
  • 自动设置属性:MyBatis会自动将获取到的主键值设置到映射文件中定义的keyProperty所指向的对象属性上。
  • 适用场景:适用于支持自增主键或序列(如MySQL的AUTO_INCREMENT、PostgreSQL的SERIAL)的数据库。

实例与细节

考虑一个简单的用户表:

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50) NOT NULL,
  email VARCHAR(100)
);

对应的Java实体类:

public class User {
    private Integer id;
    private String username;
    private String email;

    // getters and setters...
}

Mapper XML配置:

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
  INSERT INTO users (username, email)
  VALUES (#{username}, #{email})
</insert>

注意事项

  • 事务管理:确保你的插入操作在一个事务中进行,以避免部分成功的情况。
  • 批量插入:对于批量插入,你可能需要特别处理,因为getGeneratedKeys()返回的结果集可能会包含多个生成的键。可以通过<foreach>标签来实现批量插入,并通过ResultSet逐个获取生成的键。
<insert id="batchInsertUsers" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
  INSERT INTO users (username, email)
  <foreach collection="list" item="user" separator=",">
    (#{user.username}, #{user.email})
  </foreach>
</insert>

批量插入后的主键处理

List<User> users = // ... your list of users to insert
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    for (User user : users) {
        mapper.insertUser(user);
        // MyBatis will automatically set the generated ID into the 'user' object
    }
    sqlSession.commit();
} finally {
    sqlSession.close();
}

异常处理

  • 捕获异常:如果插入失败,比如违反唯一性约束,MyBatis将抛出异常,你需要捕获并处理这些异常。
  • 回滚机制:确保在发生异常时,事务能够正确回滚,以保持数据的一致性。
try {
    sqlSession.insert("insertUser", user);
    sqlSession.commit();
} catch (PersistenceException e) {
    sqlSession.rollback();
    throw e;
} finally {
    sqlSession.close();
}

性能考量

  • 减少查询次数:尽量减少额外的查询,例如在批量插入时合理使用getGeneratedKeys()
  • 优化连接池:确保JDBC连接池配置得当,以应对高并发情况下的性能需求。
  • 批处理优化:对于批量操作,可以使用JDBC的批处理功能来提高性能。
<insert id="batchInsertUsers" parameterType="java.util.List">
  INSERT INTO users (username, email)
  VALUES
  <foreach collection="list" item="user" separator=",">
    (#{user.username}, #{user.email})
  </foreach>
</insert>

<selectKey>

机制与原理

  • 自定义SQL<selectKey>允许你编写任意的SQL来生成主键值,并且可以指定这个查询是在INSERT之前还是之后执行。
  • 灵活性:它适合于任何需要特定逻辑来生成主键的情况,或者当数据库不支持自增字段时使用。

实例与细节

假设我们有一个Oracle数据库,并使用序列user_seq来生成主键:

Mapper XML配置:

<insert id="insertUserWithSequence">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    SELECT user_seq.NEXTVAL FROM dual
  </selectKey>
  INSERT INTO users (id, username, email)
  VALUES (#{id}, #{username}, #{email})
</insert>

对于某些数据库(如PostgreSQL),你可以直接使用RETURNING子句来简化代码:

<insert id="insertUserWithReturning" parameterType="User">
  INSERT INTO users (username, email)
  VALUES (#{username}, #{email})
  RETURNING id
</insert>

性能考量

  • 减少查询次数:尽量减少额外的查询,比如使用RETURNING子句代替<selectKey>
  • 缓存序列:如果使用序列生成主键,考虑在应用层实现序列缓存以减少对数据库的压力。
  • 并发控制:考虑到并发环境下的序列分配问题,确保你的序列设计能够正确处理高并发场景。

异常处理

  • 空值检查:确保在调用<selectKey>后检查返回的主键是否为空或无效。
  • 并发控制:考虑到并发环境下的序列分配问题,确保你的序列设计能够正确处理高并发场景。
  • 事务管理:确保在发生异常时,事务能够正确回滚,以保持数据的一致性。
<selectKey keyProperty="id" resultType="int" order="BEFORE">
  SELECT COALESCE(MAX(id), 0) + 1 FROM users -- 简单示例,实际生产环境中应避免这种方式
</selectKey>

数据库兼容性

不同的数据库有不同的特性和限制,比如Oracle的序列机制、MySQL的自增字段、PostgreSQL的RETURNING语法等。在设计主键生成策略时,务必考虑到目标数据库的具体特性。

  • Oracle:通常使用序列和触发器来生成主键。
  • MySQL:使用自增字段。
  • PostgreSQL:支持SERIAL类型和RETURNING子句。
  • SQL Server:使用IDENTITY列或SEQUENCE对象。

监控与日志

在生产环境中,监控主键生成的相关操作非常重要。良好的日志记录可以帮助快速定位和解决问题,特别是当涉及到并发控制或性能瓶颈时。

log.info("Generated ID for new user: {}", user.getId());

综合比较

特性/方面useGeneratedKeys<selectKey>
适用数据库支持自增主键或序列的数据库所有类型的数据库
配置复杂度简单较复杂
性能更高效,减少额外查询可能引入额外查询,影响性能
灵活性依赖数据库特性高度灵活,适应各种复杂场景
维护成本较低,易于维护较高,可能增加维护负担

注意事项

批量插入与主键生成

对于批量插入,useGeneratedKeys<selectKey>都有其特定的挑战。对于useGeneratedKeys,你需要确保正确处理结果集中返回的多个主键值。而对于<selectKey>,你可能需要为每个要插入的记录单独生成主键,这可能导致性能问题。因此,在批量插入的情况下,最好评估具体的性能需求并选择最合适的策略。

事务边界与一致性

无论是useGeneratedKeys还是<selectKey>,都应确保它们的操作在一个明确的事务边界内完成,以防止部分成功导致的数据不一致。特别是在分布式系统中,跨服务的事务管理尤为重要。

数据库兼容性

不同的数据库有不同的特性和限制,比如Oracle的序列机制、MySQL的自增字段、PostgreSQL的RETURNING语法等。在设计主键生成策略时,务必考虑到目标数据库的具体特性。

优化建议

  • 批量操作:使用JDBC的批处理功能来提高批量插入的性能。
  • 序列缓存:对于使用序列生成主键的数据库,可以在应用程序层面实现序列缓存,减少频繁访问数据库的开销。
  • 异步处理:对于大规模数据插入,可以考虑异步处理以减轻数据库压力。
  • 索引优化:确保插入操作不会导致大量索引重建,影响性能。

到此这篇关于MyBatis主键生成策略中useGeneratedKeys和<selectKey>的区别的文章就介绍到这了,更多相关MyBatis useGeneratedKeys和<selectKey>内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • SpringBoot整合logback日志的详细步骤

    SpringBoot整合logback日志的详细步骤

    这篇文章主要介绍了SpringBoot整合logback日志的详细步骤,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Maven使用集成测试的示例代码

    Maven使用集成测试的示例代码

    本文介绍了在Maven项目中使用maven-failsafe-plugin插件进行集成测试,步骤包括添加测试依赖、编写集成测试类、配置插件、运行测试以及查看和分析测试结果,感兴趣的可以了解一下
    2024-11-11
  • Java中优先队列PriorityQueue常用方法示例

    Java中优先队列PriorityQueue常用方法示例

    这篇文章主要介绍了Java中优先队列PriorityQueue常用方法示例,PriorityQueue是一种特殊的队列,满足队列的“队尾进、队头出”条件,但是每次插入或删除元素后,都对队列进行调整,使得队列始终构成最小堆(或最大堆),需要的朋友可以参考下
    2023-09-09
  • 浅谈java中Math.random()与java.util.random()的区别

    浅谈java中Math.random()与java.util.random()的区别

    下面小编就为大家带来一篇浅谈java中Math.random()与java.util.random()的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • 关于maven打包时的报错: Return code is: 501 , ReasonPhrase:HTTPS Required

    关于maven打包时的报错: Return code is: 501 , ReasonPhrase:HTTPS Requ

    这篇文章主要介绍了关于maven打包时的报错: Return code is: 501 , ReasonPhrase:HTTPS Required,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 详解MyBatis-Plus updateById方法更新不了空字符串/null解决方法

    详解MyBatis-Plus updateById方法更新不了空字符串/null解决方法

    这篇文章主要介绍了详解MyBatis-Plus updateById方法更新不了空字符串/null解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • spring boot打包成可执行jar包

    spring boot打包成可执行jar包

    本篇文章主要介绍了spring boot打包成可执行jar包,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Spring中基于xml的声明式事务示例详解

    Spring中基于xml的声明式事务示例详解

    这篇文章主要介绍了Spring之基于xml的声明式事务,本文通过图文实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • Java使用Zxing二维码生成的简单示例

    Java使用Zxing二维码生成的简单示例

    ZXing是一个开源的,用Java实现的多种格式的1D/2D条码图像处理库,下面这篇文章主要给大家介绍了关于Java使用Zxing二维码生成的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • java编程实现并查集的路径压缩代码详解

    java编程实现并查集的路径压缩代码详解

    这篇文章主要介绍了java编程实现并查集的路径压缩代码详解,具有一定借鉴价值,需要的朋友可以参考。
    2017-11-11

最新评论