You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot+MyBatis中获取Oracle触发器生成ID:返回值始终为1

问题:插入操作后返回的ID始终为1的排查与解决

我折腾了好一阵子,查了好多类似问题,但代码里就是拿不到正确的ID——每次执行插入后返回的ID都是1。我的表有两列ITEM_ID和DUMMY,是通过触发器调用序列给ITEM_ID赋值的,相关代码如下:

相关代码

ItemController.java

@Operation(description = "Insert Test")
@PostMapping("/item/add")
@ResponseBody
void upsertTest( @RequestBody ItemForm testForm ) {
    Long itemId = itemService.getItemId();
    testForm.setItemId(itemId);
    System.out.println("Item ID: " + itemId );
}

ItemService.java

@Service("ItemService")
public class ItemService {
    private final ItemDao itemDao;
    @Autowired
    public ItemService(@Qualifier("itemDao") final ItemDao itemDao) {
        this.itemDao = itemDao;
    }
    public Long getItemId(){
        return itemDao.getItemId();
    }
}

ItemDao.java

@Component("itemDao")
@Repository
public interface ItemDao {
    Long getItemId();
}

ItemDao.xml

<insert id="getItemId" useGeneratedKeys="true" keyProperty="itemId" keyColumn="ITEM_ID">
    INSERT INTO ITEM_ID_TABLE (DUMMY) VALUES (null)
</insert>

问题根源

你这里的核心问题是MyBatis的useGeneratedKeys配置和触发器生成ID的逻辑不兼容。

useGeneratedKeys是MyBatis用来获取数据库自增主键的机制,但你的主键是通过触发器调用序列生成的——这种情况下,MyBatis没法直接捕获到触发器生成的ID值。而你看到的1,其实是MyBatis返回的插入操作受影响的行数(每次插入1条数据,所以返回1),你误把这个行数当成了主键ID,才会出现每次输出都是1的情况。


解决方法

根据你的场景,我给你三种可行的解决方案,你可以根据实际情况选择:

方案1:直接在插入语句中调用序列(最推荐)

既然是用序列生成ID,不如直接在插入语句里显式调用序列的NEXTVAL,这样MyBatis就能通过useGeneratedKeys正确拿到生成的ID。

修改ItemDao.xml:

<insert id="getItemId" useGeneratedKeys="true" keyProperty="itemId" keyColumn="ITEM_ID">
    INSERT INTO ITEM_ID_TABLE (ITEM_ID, DUMMY) 
    VALUES (YOUR_SEQUENCE_NAME.NEXTVAL, null)
</insert>

注意:把YOUR_SEQUENCE_NAME替换成你实际使用的序列名称。

同时,建议调整Dao接口和Service的逻辑,通过传入对象来接收ID(更符合MyBatis的最佳实践):

  • 修改ItemDao.java:
@Component("itemDao")
@Repository
public interface ItemDao {
    void getItemId(ItemForm itemForm);
}
  • 修改ItemService.java:
public Long getItemId(ItemForm itemForm){
    itemDao.getItemId(itemForm);
    return itemForm.getItemId();
}
  • 修改ItemController.java:
@Operation(description = "Insert Test")
@PostMapping("/item/add")
@ResponseBody
void upsertTest( @RequestBody ItemForm testForm ) {
    Long itemId = itemService.getItemId(testForm);
    System.out.println("Item ID: " + itemId );
}

方案2:保留触发器,用<selectKey>获取ID

如果必须保留触发器生成ID的逻辑,那可以用MyBatis的<selectKey>标签,在插入后获取序列的当前值(CURRVAL):

修改ItemDao.xml:

<insert id="getItemId">
    <selectKey keyProperty="itemId" keyColumn="ITEM_ID" resultType="java.lang.Long" order="AFTER">
        SELECT YOUR_SEQUENCE_NAME.CURRVAL FROM DUAL
    </selectKey>
    INSERT INTO ITEM_ID_TABLE (DUMMY) VALUES (null)
</insert>

这里order="AFTER"表示先执行插入(触发器会调用NEXTVAL生成ID),再获取序列的当前值,也就是刚生成的主键ID。

同样,你需要调整Dao接口和Service的逻辑,通过传入ItemForm对象来接收ID,和方案1的调整方式一致。

方案3:修改触发器,让MyBatis能捕获ID(不推荐)

有些数据库(比如Oracle)可以通过修改触发器,让MyBatis的useGeneratedKeys能捕获到生成的ID,但这种方式比较复杂,而且依赖数据库特性,不如前两种方案稳妥,所以不推荐。


内容的提问来源于stack exchange,提问作者G Josh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:37:36