又是我,集团项目新创改造,这次其实是一个我之前处理过的,一个其实蛮简单的问题,插入获取主键
其实之前的写法大概是
之前版本
sql
<!-- 新建闲置单,将单据编号、状态插入数据库中 -->
<insert id="insertAssetIdleHeaderInFo" parameterType="cn.chinaunicom.ams.assetIdle.entity.AssetIdle">
insert into cams_idle_header_all
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="bookTypeCode != null">
BOOK_TYPE_CODE,
</if>
<if test="billNumber != null">
IDLE_NUMBER,
</if>
<if test="billTitle != null and billTitle != '' ">
IDLE_TITLE,
</if>
<if test="areaCode != null" >
IDLE_DEPT_CODE,
</if>
<if test="areaName != null">
IDLE_DEPT_NAME,
</if>
<if test="status != null">
STATUS,
</if>
LAST_UPDATE_DATE,
<if test="staffId != null">
DOC_EMPLOYEE_NUMBER,
LAST_UPDATED_BY,
CREATED_BY,
</if>
<if test="staffName != null">
DOC_EMPLOYEE_NAME,
</if>
CREATION_DATE,
<if test="bgId != null">
BG_ID,
</if>
<if test="ouId != null">
ORG_ID,
</if>
<if test="dpId != null and dpId != ''">
DEPT_ID,
</if>
<if test="createdDuty != null">
created_Duty,
</if>
<if test="ouName != null">
ou_name,
</if>
<if test="createRoleId != null">
CREATE_ROLE_ID,
</if>
<if test="staffErpCode != null and staffErpCode != ''">
STAFF_ERP_CODE,
</if>
<if test="idleDate != null and idleDate != ''">
IDLE_DATE,
</if>
<if test="idleNote != null and idleNote != ''">
IDLE_NOTE,
</if>
<if test="completedBy != null and completedBy != ''">
COMPLETED_BY,
</if>
<if test="completeDate != null and completeDate != ''">
COMPLETE_DATE,
</if>
<if test="periodName != null and periodName != ''">
PERIOD_NAME,
</if>
<if test="approvedBy != null and approvedBy != ''">
APPROVED_BY,
</if>
<if test="approvedDate != null and approvedDate != ''">
APPROVED_DATE,
</if>
<if test="specialType != null and specialType != ''">
SPECIAL_TYPE,
</if>
<if test="specialId != null and specialId != ''">
SPECIAL_ID,
</if>
<if test="specialSubclass != null and specialSubclass != ''">
SPECIAL_SUBCLASS,
</if>
<if test="specialSubclassId != null and specialSubclassId != ''">
SPECIAL_SUBCLASS_ID,
</if>
<if test="assetStatus != null and assetStatus != ''">
ASSET_STATUS,
</if>
<if test="assetSubStatus != null and assetSubStatus != ''">
ASSET_SUB_STATUS,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="bookTypeCode != null">
#{bookTypeCode,jdbcType=VARCHAR},
</if>
<if test="billNumber != null">
#{billNumber,jdbcType=VARCHAR},
</if>
<if test="billTitle != null and billTitle != '' ">
#{billTitle,jdbcType=VARCHAR},
</if>
<if test="areaCode != null">
#{areaCode,jdbcType=VARCHAR},
</if>
<if test="areaName != null">
#{areaName,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=VARCHAR},
</if>
current_timestamp(),
<if test="staffId != null">
#{staffId,jdbcType=BIGINT},
#{staffId,jdbcType=BIGINT},
#{staffId,jdbcType=BIGINT},
</if>
<if test="staffName != null">
#{staffName,jdbcType=VARCHAR},
</if>
current_timestamp(),
<if test="bgId != null">
#{bgId},
</if>
<if test="ouId != null">
#{ouId},
</if>
<if test="dpId != null and dpId != ''">
#{dpId},
</if>
<if test="createdDuty != null">
#{createdDuty},
</if>
<if test="ouName != null">
#{ouName},
</if>
<if test="createRoleId != null">
#{createRoleId},
</if>
<if test="staffErpCode != null and staffErpCode != ''">
#{staffErpCode},
</if>
<if test="idleDate != null and idleDate != ''">
#{idleDate},
</if>
<if test="idleNote != null and idleNote != ''">
#{idleNote},
</if>
<if test="completedBy != null and completedBy != ''">
#{completedBy},
</if>
<if test="completeDate != null and completeDate != ''">
#{completeDate},
</if>
<if test="periodName != null and periodName != ''">
#{periodName},
</if>
<if test="approvedBy != null and approvedBy != ''">
#{approvedBy},
</if>
<if test="approvedDate != null and approvedDate != ''">
#{approvedDate},
</if>
<if test="specialType != null and specialType != ''">
#{specialType},
</if>
<if test="specialId != null and specialId != ''">
#{specialId},
</if>
<if test="specialSubclass != null and specialSubclass != ''">
#{specialSubclass},
</if>
<if test="specialSubclassId != null and specialSubclassId != ''">
#{specialSubclassId},
</if>
<if test="assetStatus != null and assetStatus != ''">
#{assetStatus},
</if>
<if test="assetSubStatus != null and assetSubStatus != ''">
#{assetSubStatus},
</if>
</trim>
<selectKey resultType="java.lang.Long" keyProperty="headerId" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
这是之前的mysql 的写法,
sql
<!-- 新建闲置单,将单据编号、状态插入数据库中 -->
<insert id="insertAssetIdleHeaderInFo" parameterType="cn.chinaunicom.ams.assetIdle.entity.AssetIdle"
useGeneratedKeys="true" keyProperty="headerId">insert into cams_idle_header_all
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="bookTypeCode != null">
BOOK_TYPE_CODE,
</if>
<if test="billNumber != null">
IDLE_NUMBER,
</if>
<if test="billTitle != null and billTitle != '' ">
IDLE_TITLE,
</if>
<if test="areaCode != null" >
IDLE_DEPT_CODE,
</if>
<if test="areaName != null">
IDLE_DEPT_NAME,
</if>
<if test="status != null">
STATUS,
</if>
LAST_UPDATE_DATE,
<if test="staffId != null">
DOC_EMPLOYEE_NUMBER,
LAST_UPDATED_BY,
CREATED_BY,
</if>
<if test="staffName != null">
DOC_EMPLOYEE_NAME,
</if>
CREATION_DATE,
<if test="bgId != null">
BG_ID,
</if>
<if test="ouId != null">
ORG_ID,
</if>
<if test="dpId != null and dpId != ''">
DEPT_ID,
</if>
<if test="createdDuty != null">
created_Duty,
</if>
<if test="ouName != null">
ou_name,
</if>
<if test="createRoleId != null">
CREATE_ROLE_ID,
</if>
<if test="staffErpCode != null and staffErpCode != ''">
STAFF_ERP_CODE,
</if>
<if test="idleDate != null and idleDate != ''">
IDLE_DATE,
</if>
<if test="idleNote != null and idleNote != ''">
IDLE_NOTE,
</if>
<if test="completedBy != null and completedBy != ''">
COMPLETED_BY,
</if>
<if test="completeDate != null and completeDate != ''">
COMPLETE_DATE,
</if>
<if test="periodName != null and periodName != ''">
PERIOD_NAME,
</if>
<if test="approvedBy != null and approvedBy != ''">
APPROVED_BY,
</if>
<if test="approvedDate != null and approvedDate != ''">
APPROVED_DATE,
</if>
<if test="specialType != null and specialType != ''">
SPECIAL_TYPE,
</if>
<if test="specialId != null and specialId != ''">
SPECIAL_ID,
</if>
<if test="specialSubclass != null and specialSubclass != ''">
SPECIAL_SUBCLASS,
</if>
<if test="specialSubclassId != null and specialSubclassId != ''">
SPECIAL_SUBCLASS_ID,
</if>
<if test="assetStatus != null and assetStatus != ''">
ASSET_STATUS,
</if>
<if test="assetSubStatus != null and assetSubStatus != ''">
ASSET_SUB_STATUS,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="bookTypeCode != null">
#{bookTypeCode,jdbcType=VARCHAR},
</if>
<if test="billNumber != null">
#{billNumber,jdbcType=VARCHAR},
</if>
<if test="billTitle != null and billTitle != '' ">
#{billTitle,jdbcType=VARCHAR},
</if>
<if test="areaCode != null">
#{areaCode,jdbcType=VARCHAR},
</if>
<if test="areaName != null">
#{areaName,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=VARCHAR},
</if>
current_timestamp(),
<if test="staffId != null">
#{staffId,jdbcType=BIGINT},
#{staffId,jdbcType=BIGINT},
#{staffId,jdbcType=BIGINT},
</if>
<if test="staffName != null">
#{staffName,jdbcType=VARCHAR},
</if>
current_timestamp(),
<if test="bgId != null">
#{bgId},
</if>
<if test="ouId != null">
#{ouId},
</if>
<if test="dpId != null and dpId != ''">
#{dpId},
</if>
<if test="createdDuty != null">
#{createdDuty},
</if>
<if test="ouName != null">
#{ouName},
</if>
<if test="createRoleId != null">
#{createRoleId},
</if>
<if test="staffErpCode != null and staffErpCode != ''">
#{staffErpCode},
</if>
<if test="idleDate != null and idleDate != ''">
#{idleDate},
</if>
<if test="idleNote != null and idleNote != ''">
#{idleNote},
</if>
<if test="completedBy != null and completedBy != ''">
#{completedBy},
</if>
<if test="completeDate != null and completeDate != ''">
#{completeDate},
</if>
<if test="periodName != null and periodName != ''">
#{periodName},
</if>
<if test="approvedBy != null and approvedBy != ''">
#{approvedBy},
</if>
<if test="approvedDate != null and approvedDate != ''">
#{approvedDate},
</if>
<if test="specialType != null and specialType != ''">
#{specialType},
</if>
<if test="specialId != null and specialId != ''">
#{specialId},
</if>
<if test="specialSubclass != null and specialSubclass != ''">
#{specialSubclass},
</if>
<if test="specialSubclassId != null and specialSubclassId != ''">
#{specialSubclassId},
</if>
<if test="assetStatus != null and assetStatus != ''">
#{assetStatus},
</if>
<if test="assetSubStatus != null and assetSubStatus != ''">
#{assetSubStatus},
</if>
</trim>
</insert>
这个是达梦数据库的写法,其实就是一个简单的问题,
<selectKey resultType="java.lang.Long" keyProperty="headerId" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey>修改成useGeneratedKeys="true" keyProperty="headerId"的写法,其实这个不好使,LAST_INSERT_ID()是因为这个函数不好使,而不是selectKey 不好使,之前我也是不知道,为什么我知道了呢,是因为下面这个问题。
java
/**
* 复制流程
* ict使用-流程配置
*
* @throws Exception
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String addFlow(FlowCfgType type) throws Exception {
// 判断是复制还是升级(1:升级;0:复制)
Short isUpgrade = type.getIsUpgrade();
if (isUpgrade == null || (isUpgrade != 1 && isUpgrade != 0)) {
throw new RuntimeException("升级和复制功能不明确,请重新操作");
}
//type表复制
int a = flowCfgTypeMapper.addCopyType(type);
String flowTypeId = null;
if (a > 0) {
flowTypeId = flowCfgTypeMapper.selectLastId();
}
type.setFlowTypeId(flowTypeId);
//获取当前用户ip
// String ip = IPCheck.getIpAddr(request);
InetAddress localhost = NetUtil.getLocalhost();
String ip = localhost.getHostAddress();
//插入一条历史复制记录
flowCfgLogMapper.addCopyTypeOperHst(type.getFlowTypeId(), WebUtil.getNowUser().getLoginName(), "c", ip, type.getOriginalTypeId());
//查询原始的节点的id
List<FlowCfgLink> originalList = flowCfgLinkMapper.selectOriginalFlow(type.getOriginalTypeId());
if (originalList == null || originalList.isEmpty()) {
throw new Exception("linkError");
}
flowCfgLinkMapper.addCopylink(type);//link表复制
//flowCfgLinkMapper.addCopylinkXy(type);//linkXy表复制 该表已不使用
//查询复制后的节点的id
List<FlowCfgLink> newList = flowCfgLinkMapper.selectNewFlow(type.getFlowTypeId());
Map<String, String> linkMap = new HashMap<>();
List<String> linkIds = new ArrayList<String>();
for (FlowCfgLink oLink : originalList) {
for (FlowCfgLink nLink : newList) {
if (oLink.getLinkKey().equals(nLink.getLinkKey())) {
linkMap.put(oLink.getFlowLinkId(), nLink.getFlowLinkId());
}
}
linkIds.add(oLink.getFlowLinkId());
}
List<FlowCfgPath> pathList = flowCfgPathMapper.selectPath4LinkId(linkIds);
if (pathList == null || pathList.isEmpty()) {
throw new Exception("pathError");
}
for (FlowCfgPath flowCfgPath : pathList) {
flowCfgPath.setFlowLinkId(linkMap.get(flowCfgPath.getFlowLinkId()));
if (!flowCfgPath.getToFlowLinkId().contains("endevent")) {
flowCfgPath.setToFlowLinkId(linkMap.get(flowCfgPath.getToFlowLinkId()));
}
//路径表复制
flowCfgPathMapper.addCopyPath(flowCfgPath);
}
// 由于参与方式涉及到组织等,复制后注意参与者,需手动修改不符合组织的参与者
List<FlowCfgJoin> joinList = flowCfgLinkMapper.selectJoin4LinkId(linkIds);
if (joinList != null && !joinList.isEmpty()) {
for (FlowCfgJoin flowCfgJoin : joinList) {
flowCfgJoin.setFlowLinkId(linkMap.get(flowCfgJoin.getFlowLinkId()));
}
//参与属性表复制
flowCfgLinkMapper.addCopyJoin(joinList);
if (isUpgrade == 1) {
List<FlowCfgJoin> idList = flowCfgLinkMapper.selectJoinId(type);
// 如果为升级,则复制join_ext表
List<FlowCfgJoinExt> joinExtList = flowCfgLinkMapper.selectJoinExt4JoinIds(idList);
if (joinExtList != null && !joinExtList.isEmpty()) {
List<FlowCfgJoin> newJoinList = flowCfgLinkMapper.selectJoinList(idList);
if (newJoinList != null && !newJoinList.isEmpty()) {
for (FlowCfgJoinExt joinExt : joinExtList) {
for (FlowCfgJoin flowCfgJoin : joinList) {
if (joinExt.getJoinId().equals(flowCfgJoin.getFlowJoinId())) {
for (FlowCfgJoin newJoin : newJoinList) {
if (flowCfgJoin.getFlowLinkId().equals(newJoin.getFlowLinkId())) {
joinExt.setJoinId(newJoin.getFlowJoinId());
}
}
}
}
}
//参与属性扩展表复制
flowCfgLinkMapper.addCopyJoinExt(joinExtList);
}
}
}
}
return type.getFlowTypeId();
}
这个功能大概就是在一个复制一个工作流,其实蛮尴尬的,就是//type表复制 int a = flowCfgTypeMapper.addCopyType(type); String flowTypeId = null; if (a > 0) { flowTypeId = flowCfgTypeMapper.selectLastId(); }
我觉得还是蛮炸裂的,确实是一个是创建一条记录,一个是创建记录了之后,去获取最新的记录,但是吧,因为上面的写法,我觉得可以在插入的时候直接拿到对应的值把,不过该说不说这个写法是真的蠢啊,真的不考虑高并发的么,然后我想变成一个mapper直接去处理,
java
<insert id="addCopyType" parameterType="cn.chinaunicom.workflow.center.expand.entity.FlowCfgType">
insert into FLOW_CFG_TYPE
(flow_type_id,FLOW_KEY,FLOW_KEY_REV,FLOW_NAME,URL,HIST_URL,
STATUS,STARTTIME,BG_ID,ORG_ID)
select (select flow_type_id from flow_cfg_type order by flow_type_id desc FETCH FIRST 1 ROWS ONLY)+1,t.FLOW_KEY,
#{flowKeyRev,jdbcType=VARCHAR},#{flowName,jdbcType=VARCHAR},t.URL,t.HIST_URL, 0,SYSDATE,
<if test="bgId!=0">
#{bgId,jdbcType=INTEGER},
</if>
<if test="bgId==0">
t.BG_ID,
</if>#{orgId,jdbcType=INTEGER} from FLOW_CFG_TYPE t WHERE
t.FLOW_TYPE_ID = #{originalTypeId,jdbcType=VARCHAR}
</insert>
这是他原本的写法
然后我想转换成可以直接获取id的写法,并且因为id是主键而且是自增的,主键也不需要去进行插入,可以将这操作直接交给数据库,
sql
<insert id="addCopyType" parameterType="cn.chinaunicom.workflow.center.expand.entity.FlowCfgType"
useGeneratedKeys="true" keyProperty="flowTypeId">
insert into FLOW_CFG_TYPE
(FLOW_KEY,
FLOW_KEY_REV,
FLOW_NAME,
URL,
HIST_URL,
STATUS,
STARTTIME,
BG_ID,
ORG_ID)
select t.FLOW_KEY,
#{flowKeyRev,jdbcType=VARCHAR},
#{flowName,jdbcType=VARCHAR},
t.URL,
t.HIST_URL,
0,
SYSDATE,
<if test="bgId!=0">
#{bgId,jdbcType=INTEGER},
</if>
<if test="bgId==0">
t.BG_ID,
</if>#{orgId,jdbcType=INTEGER}
from FLOW_CFG_TYPE t WHERE
t.FLOW_TYPE_ID = #{originalTypeId,jdbcType=VARCHAR}
</insert>
理论上来说这样是可信的,但是我确实看到了flowTypeId值是0,贼尴尬,我不知道为啥,然后我去问了问ai。
ai
为什么 flowTypeId 返回 0?
useGeneratedKeys="true" 只对数字类型的主键有效,具体限制如下:
✅ 支持: NUMBER、INTEGER、BIGINT 等数字类型(配合数据库自增或序列)
❌ 不支持: VARCHAR、CHAR 等字符类型
对于 insert into ... select ... 语法:
MyBatis 无法通过 useGeneratedKeys 获取生成的主键
因为这不是简单的单行插入,而是从查询结果插入
useGeneratedKeys="true" 在您的场景下无法正常工作,原因有三:
字段类型不匹配: FLOW_TYPE_ID 是 VARCHAR 类型,而 useGeneratedKeys 主要支持数字类型主键
SQL语法限制: insert into ... select ... 语法不支持返回生成的主键
缺少主键生成机制: 您的SQL没有显式指定 FLOW_TYPE_ID 的值
然后 我改了,挺麻烦的,给String类型修改成int
但是问题又来了,还是显示不大对,还是显示0,
然后我不是很理解 我又去问了问ai,结果就是
ai
🎯 达梦数据库的特殊性
达梦数据库虽然兼容 Oracle 和 MySQL,但在以下场景中 useGeneratedKeys 无法正常工作:
❌ insert into ... select ... 语法 - 您当前的情况
❌ 批量插入
⚠️ 某些复杂插入场景
达梦的 useGeneratedKeys 支持情况
| SQL 类型 | useGeneratedKeys 支持 | 说明 |
|---|---|---|
| INSERT INTO ... VALUES(...) | ✅ 支持 | 简单插入 |
| INSERT INTO ... SELECT ... | ❌ 不支持 | 子查询插入 |
| 批量插入 | ⚠️ 部分支持 | 需要特殊配置 |
| 实话说我是没想到,SELECT 不支持!!!! | ||
| 然后通过ai修改了代码 |
sql
<insert id="addCopyType" parameterType="cn.chinaunicom.workflow.center.expand.entity.FlowCfgType"
useGeneratedKeys="true" keyProperty="flowTypeId">
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="flowTypeId">
SELECT MAX(FLOW_TYPE_ID) FROM FLOW_CFG_TYPE
</selectKey>
insert into FLOW_CFG_TYPE
(FLOW_KEY,
FLOW_KEY_REV,
FLOW_NAME,
URL,
HIST_URL,
STATUS,
STARTTIME,
BG_ID,
ORG_ID)
select t.FLOW_KEY,
#{flowKeyRev,jdbcType=VARCHAR},
#{flowName,jdbcType=VARCHAR},
t.URL,
t.HIST_URL,
0,
SYSDATE,
<if test="bgId!=0">
#{bgId,jdbcType=INTEGER},
</if>
<if test="bgId==0">
t.BG_ID,
</if>#{orgId,jdbcType=INTEGER}
from FLOW_CFG_TYPE t WHERE
t.FLOW_TYPE_ID = #{originalTypeId,jdbcType=VARCHAR}
</insert>
然后就是完整的写法