Java中last_insert_id的使用

本文介绍了一种通过last_insert_id方法在插入新记录后获取自增主键ID的方法,并提供了一个使用Java和Spring框架实现的例子,展示了如何避免主键不确定性的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

last_insert_id的作用是:在当前表中的主键是自增时,插入一条新记录的同时使用last_insert_id可以获取当前的新记录的主键id。

下面是一个例子:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml","classpath:applicationContext.xml"})
public class TestSql {
    @Autowired(required = true) 
    private JdbcTemplate jdbcTemplate; 
    @Autowired(required = true) 
    private JdbcTemplate jdbcTemplate1; 
    
    @Test
    public void select(){
        final String sql = "insert into series(class_id,brand_id,series_name) values("+2+","+2+",'"+ "sfsfds')";
        System.out.println(sql);
        
        /*
        //这种方式会出现主键不确定性(当在另一张表同时插入一条数据并也要获取主键时会出现重复或者为0的情况)
        int ret = jdbcTemplate.update(sql);
        System.out.println("系列返回值==="+ret);
        String last = "select last_insert_id()";
        int lastId = jdbcTemplate.queryForInt(last);
        System.out.println("最新系列ID===="+lastId);
        */
        
        KeyHolder keyHolder = new GeneratedKeyHolder();  
        //这种方式是在上一种方式之上优化其不确定性(避免出现上述重复或者为0的情况)
        int i = jdbcTemplate.update(new PreparedStatementCreator() {  
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {  
                return con.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
                //or return con.prepareStatement(sql, new String[]{"ID"});//ID是自动增长的自动  
            }  
        }, keyHolder);  
        System.out.println(i);
        System.out.println(keyHolder.getKeyList());
        
        final String sql1 = "insert into meta_type(series_id,type_name) values("+2+",'"+ "sfsfds')";
        System.out.println(sql1);
//        KeyHolder keyHolder1 = new GeneratedKeyHolder();  
        int j = jdbcTemplate1.update(new PreparedStatementCreator() {  
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {  
                return con.prepareStatement(sql1, PreparedStatement.RETURN_GENERATED_KEYS);  
                //or return con.prepareStatement(sql, new String[]{"ID"});//ID是自动增长的自动  
            }  
        }, keyHolder);  
        System.out.println("----"+j);  
        System.out.println(keyHolder.getKeyList());
    }
    
}

 

转载于:https://www.cnblogs.com/smart-hwt/p/8256864.html

<think>首先,用户的问题是:“请问mybatis批量插入onduplicatekeyupdatelast_insert_id获取自增id”。用户想知道在MyBatis中使用批量插入,结合ONDUPLICATEKEYUPDATE和LAST_INSERT_ID()函数是否能获取自增ID。从引用中,我有几个关键点:-引用[1]:提到在使用INSERT...ONDUPLICATEKEYUPDATE,SELECTLAST_INSERT_ID()可能返回的是本表的自增ID,而是上一次INSERTID。这是因为当有唯一约束导致更新会产生新的ID,所以LAST_INSERT_ID()返回的是上一个INSERTID。-引用[2]:MySQL文档说明,LAST_INSERT_ID()返回的是第一个插入行的值,如果插入多行。-引用[3]:在MyBatis中使用ONDUPLICATEKEYUPDATE进行批量插入,可能只有第一条数据返回主键,因为LAST_INSERT_ID()只返回第一个插入行的ID。也提到在并行事务中可能出现死锁。-引用[4]:MyBatis批量插入返回主键的示例,但结合ONDUPLICATEKEYUPDATE可能有问题。所以,核心问题是:在批量插入中使用ONDUPLICATEKEYUPDATELAST_INSERT_ID()的行为可能导致无法正确获取所有自增ID。现在,我需要构建回答。回答结构要清晰,帮助用户逐步解决问题。使用中文回答。回答结构:1.**解释问题**:简要说明ONDUPLICATEKEYUPDATE和LAST_INSERT_ID()的作用。2.**分析行为**:解释在批量插入中,LAST_INSERT_ID()的行为,特别是当有更新。3.**MyBatis中的实现**:讨论如何在MyBatis中配置批量插入和获取自增ID。4.**潜在问题**:指出为什么在ONDUPLICATEKEYUPDATE场景下获取所有自增ID是困难的。5.**解决方案或建议**:提供可能的解决方法。6.**生成相关问题**:根据内容生成相关问题。确保LaTeX格式正确:行内数学表达式用$...$,独立公式用$$...$$并单独成段。在这个上下文中,可能需要数学公式,但如果有计算或表达式,就使用。添加引用标识:在引用引用内容,末尾添加[^1]、[^2]等。参考用户引用的内容:-[^1]:解释LAST_INSERT_ID()在ONDUPLICATEKEYUPDATE下的行为。-[^2]:MySQL文档说明LAST_INSERT_ID()只返回第一个插入行的ID。-[^3]:MyBatis中使用ONDUPLICATEKEYUPDATE的坑。-[^4]:MyBatis批量插入返回主键的配置。现在,构建回答:-**开头**:直接回答用户问题。-**主体**:-解释ONDUPLICATEKEYUPDATE:用于插入或更新。-解释LAST_INSERT_ID():返回最后插入的自增ID。-在批量插入中,LAST_INSERT_ID()只返回第一个插入行的ID。-当使用ONDUPLICATEKEYUPDATE,如果有更新,LAST_INSERT_ID()可能返回无效值(如上一次INSERTID)。-在MyBatis中,配置useGeneratedKeys="true"keyProperty="id"可以获取自增ID,但在批量插入和ONDUPLICATEKEYUPDATE下可能工作正常。-建议:避免依赖数据库返回所有ID使用其他方式生成ID(如UUID);或者只获取插入行的ID。-**结尾**:总结并生成相关问题。生成相关问题:1.在MyBatis中如何正确实现批量插入并获取自增ID?2.ONDUPLICATEKEYUPDATE在并发环境下有哪些风险?3.除了LAST_INSERT_ID(),还有其他方法获取MySQL的自增ID吗?确保回答真实可靠:基于引用和一般知识。</think>### MyBatis批量插入中使用ON DUPLICATE KEY UPDATE和LAST_INSERT_ID()获取自增ID的分析 在MyBatis中进行批量插入,结合`ON DUPLICATE KEY UPDATE`和`LAST_INSERT_ID()`函数来获取自增ID存在一些挑战。以下我将逐步解释其行为、问题根源和解决方案,确保回答基于MySQL和MyBatis的官方文档及实际应用经验。 #### 1. **基本概念和机制** - **ON DUPLICATE KEY UPDATE**:这是MySQL的扩展语法,用于在插入数据,如果唯一约束(主键或唯一索引)冲突,则执行更新操作,否则执行插入。例如: ```sql INSERT INTO table (id, name) VALUES (1, 'A') ON DUPLICATE KEY UPDATE name = VALUES(name); ``` 如果`id=1`已存在,则更新`name`;否则插入新行。 - **LAST_INSERT_ID()**:MySQL函数,返回最后插入行的自增ID值。但关键限制是: - 它只返回批量插入中**第一个插入行**的自增ID(非更新行)[^2]。 - 在`ON DUPLICATE KEY UPDATE`场景下,如果操作导致更新而非插入,`LAST_INSERT_ID()`可能返回**上一次INSERT操作的ID**,而非本表的新ID[^1]。 - **MyBatis配置**:通常通过`useGeneratedKeys="true" keyProperty="id"`获取自增ID,但在批量操作中需谨慎。 #### 2. **在批量插入中的行为分析** 当使用MyBatis执行批量插入(如传入`List`)并包含`ON DUPLICATE KEY UPDATE`: - **插入新行**:如果某行未触发唯一约束冲突,MyBatis能正确获取该行的自增ID,通过`LAST_INSERT_ID()`返回。 - **更新现有行**:如果唯一约束冲突导致更新操作: - 会生成新自增ID(自增列值变)。 - `LAST_INSERT_ID()`返回的是**最近一次成功插入操作**的ID,可能来自其他表或历史操作,而非本行ID[^1][^3]。 - **批量插入的返回值**: - MySQL的`LAST_INSERT_ID()`仅返回批量中**第一个插入行**的ID(即使有多个插入)[^2]。 - 在MyBatis中,配置`useGeneratedKeys`后,通常只有**第一条数据**的ID能正确映射到Java对象,后续更新行或批量中的其他行ID可能丢失或错误[^3][^4]。 例如,假设批量插入3行数据: - 行1:插入新行 → 获取新ID。 - 行2:更新现有行 → `LAST_INSERT_ID()`返回行1的ID(或上一次操作ID)。 - 行3:插入新行 → 但`LAST_INSERT_ID()`仍可能返回行1的ID。 这导致返回的ID列表完整或正确。 #### 3. **问题根源** - **ID返回机制限制**:`LAST_INSERT_ID()`设计为单行插入的辅助函数,适合批量混合操作(插入+更新)[^2]。 - **MyBatis实现缺陷**:MyBatis的批量插入插件(如`insertUpdateSelective`)可能错误复用`SELECT LAST_INSERT_ID()`语句,导致返回无效ID[^1]。 - **并发风险**:在高并发下,`ON DUPLICATE KEY UPDATE`可能引发死锁或ID冲突,进一步干扰ID获取[^3]。 #### 4. **解决方案和建议** 针对获取所有自增ID的需求,推荐以下方法: - **避免依赖LAST_INSERT_ID()**: - 对于纯插入操作(无更新),使用MyBatis的批量插入配置: ```xml <insert id="batchInsert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO table (name) VALUES <foreach item="item" collection="list" separator=","> (#{item.name}) </foreach> </insert> ``` 这会返回所有新插入行的ID(但需确保无唯一约束冲突)。 - 但对于`ON DUPLICATE KEY UPDATE`,**建议依赖数据库返回ID**,因为更新行无新ID生成[^3][^4]。 - **使用业务层生成ID**: - 改用UUID或Snowflake算法在Java层生成唯一ID,避免依赖数据库自增列。例如: ```java // 在实体类中设置UUID item.setId(UUID.randomUUID().toString()); ``` 这样,无论插入或更新,都能直接获取ID。 - **分步处理**: 1. 先执行插入操作,获取新插入行的ID。 2. 再执行更新操作(使用`UPDATE`语句),但需额外查询受影响行的ID。 - **MySQL 8.0+的替代方案**:使用`RETURNING`子句(MySQL 8.0.1+支持),直接返回受影响行的ID: ```sql INSERT INTO table (id, name) VALUES (1, 'A') ON DUPLICATE KEY UPDATE name = VALUES(name) RETURNING id; ``` 但需MyBatis 3.5+版本支持此语法。 - **其他技巧**:在更新语句后添加`AND id = LAST_INSERT_ID(id)`,强制返回受影响行的ID,但可能适用于批量操作[^1]。 #### 5. **总结** 在MyBatis批量插入中结合`ON DUPLICATE KEY UPDATE`,`LAST_INSERT_ID()`无法可靠获取所有自增ID,因为它只返回第一个插入行的ID或历史值。推荐使用业务层生成ID或分步处理来规避此问题。如果必须用自增ID,确保操作纯插入(无更新)并测试并发场景[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值