Spring Boot+MyBatis中获取Oracle触发器生成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

