Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在同一事务中删除和插入会在JDBC中引发重复条目异常_Java_Mysql_Jdbc - Fatal编程技术网

Java 在同一事务中删除和插入会在JDBC中引发重复条目异常

Java 在同一事务中删除和插入会在JDBC中引发重复条目异常,java,mysql,jdbc,Java,Mysql,Jdbc,我有一个表inbox\u参与者,其inbox\u id作为外键。更新收件箱时,可能会添加或删除参与者。我正在尝试删除给定收件箱id的所有收件箱参与者,并重新插入具有相同参与者id的更新参与者。它在我的本地计算机上正常运行。但在服务器中,它为本应删除的收件箱参与者id提供了重复条目例外 更新收件箱 删除参与者 插入更新的参与者 private boolean updateParticipants(最终连接连接, 最终字符串id, 最终地图参与者, 最终TreeLabProto.Debug(调试){

我有一个表inbox\u参与者,其inbox\u id作为外键。更新收件箱时,可能会添加或删除参与者。我正在尝试删除给定收件箱id的所有收件箱参与者,并重新插入具有相同参与者id的更新参与者。它在我的本地计算机上正常运行。但在服务器中,它为本应删除的收件箱参与者id提供了重复条目例外

更新收件箱 删除参与者 插入更新的参与者
private boolean updateParticipants(最终连接连接,
最终字符串id,
最终地图参与者,
最终TreeLabProto.Debug(调试){
info(调试,“更新收件箱参与者”);
final String query=“插入收件箱参与者(收件箱参与者id、收件箱id、帐户id、`role`、创建时、更新时、通知类型、`left`)值(?、、、、、、、、、、?、?)”;
try(PreparedStatement pst=dataSource.prepareStatement(query,conn)){
调试(调试、查询、,
查询);
for(Map.Entry:participants.entrySet()){
最终var参与者=entry.getValue();
pst.setString(1,getId(参与者));
pst.设置管柱(2,id);
setString(3,entry.getKey());
setInt(4,participant.getRoleValue());
setLong(5,TreeleafDate.timestamp());
setLong(6,TreeleafDate.timestamp());
setInt(7,participant.getNotificationTypeValue());
setInt(8,participant.getParticipantStatusValue());
pst.addBatch();
}
int[]ints=pst.executeBatch();
return ints.length==participants.size()&&
Arrays.stream(ints).allMatch(value->value==1);
}catch(jdbception | SQLException e){
错误(调试,“更新参与者时出错”,e);
返回false;
}
}

如果我理解这里的一般逻辑,那么上面的代码似乎正在尝试添加(插入)或更新,首先删除,然后根据参与者是否已经存在而插入

如果这是您要做的事情的要点,那么您应该研究使用UPSERT语法。这是一种将这种类型的“插入或更新(如果已经存在)”逻辑推送到DML中的方法,这种逻辑通常更容易在代码中编写(代码更少),也更容易在代码之外的SQL控制台中测试

下面是一个关于Upserts的参考指南示例。

因此,在您的代码中,此策略将允许您删除“removeParticipants”作为一种方法,然后将INSERT_INBOX_PARTICIPANT作为一种直接插入,例如
INSERT INTO INBOX_PARTICIPANT
,而不是像这样插入INBOX_PARTICIPANT

MERGE INTO INBOX_PARTICIPANT T USING 
(VALUES
    (?, ?, ?)
) S (INBOX_ID, PARTICIPANT_ID, SOME_DATA) 
ON T.INBOX_ID = S.INBOX_ID and T.PARTICIPANT_ID= S.PARTICIPANT_ID
WHEN MATCHED THEN UPDATE SET SOME_DATA = S.SOME_DATA
WHEN NOT MATCHED THEN INSERT (INBOX_ID, PARTICIPANT_ID, SOME_DATA) 
VALUES (S.INBOX_ID, S.PARTICIPANT_ID, S.SOME_DATA);
**注意:确切的语法因基础数据库而异!详情请参见第页**

这可能会间接地解决您的问题,但也会使代码更易于维护

至于你是否想继续你原来的问题,我会调查你的删除是否在不经意间被回滚

或者,可能是因为您在批处理模式下插入,但作为直接执行更新删除,“批处理”模式插入可能获得与删除不同的事务上下文,因为“批处理模式”可能试图启动单独的事务来管理批处理。为了测试这个理论,可以尝试使删除和添加在同一批上下文中运行


另外,如果removeParticipants没有要删除的现有参与者,则看起来您调用了rollback,因为i==0。这是您想要的吗?

请添加查询。插入\u收件箱\u参与者以供查看。
private boolean removeParticipants(final Connection conn,
                                   final String id,
                                   final TreeleafProto.Debug debug) {
    logger.info(debug, "Deleting inbox participants. Inbox id : {}", id);
    final String query = "DELETE FROM inbox_participant WHERE inbox_id=?";

    try (PreparedStatement pst = dataSource.prepareStatement(query, conn)) {
        logger.debug(debug, QUERY,
                query);
        pst.setString(1, id);
        final var i = pst.executeUpdate();
        logger.debug(debug, "Delete query rows updated : {}", i);
        return i >= 0;
    } catch (JDBCException | SQLException e) {
        logger.error(debug, "Error while removing participants.", e);
        return false;
    }
}
private boolean updateParticipants(final Connection conn,
                                   final String id,
                                   final Map<String, InboxProto.InboxParticipant> participants,
                                   final TreeleafProto.Debug debug) {
    logger.info(debug, "Updating inbox participants");
    final String query = "INSERT INTO inbox_participant (inbox_participant_id, inbox_id, account_id, `role`, created_at, updated_at, notification_type, `left`) VALUES(?, ?, ?, ?, ?, ?, ?, ?)";

    try (PreparedStatement pst = dataSource.prepareStatement(query, conn)) {
        logger.debug(debug, QUERY,
                query);
        for (Map.Entry<String, InboxProto.InboxParticipant> entry : participants.entrySet()) {
            final var participant = entry.getValue();
            pst.setString(1, getId(participant));
            pst.setString(2, id);
            pst.setString(3, entry.getKey());
            pst.setInt(4, participant.getRoleValue());
            pst.setLong(5, TreeleafDate.timestamp());
            pst.setLong(6, TreeleafDate.timestamp());
            pst.setInt(7, participant.getNotificationTypeValue());
            pst.setInt(8, participant.getParticipantStatusValue());
            pst.addBatch();
        }
        int[] ints = pst.executeBatch();
        return ints.length == participants.size() &&
                Arrays.stream(ints).allMatch(value -> value == 1);
    } catch (JDBCException | SQLException e) {
        logger.error(debug, "Error while updating participants.", e);
        return false;
    }
}
MERGE INTO INBOX_PARTICIPANT T USING 
(VALUES
    (?, ?, ?)
) S (INBOX_ID, PARTICIPANT_ID, SOME_DATA) 
ON T.INBOX_ID = S.INBOX_ID and T.PARTICIPANT_ID= S.PARTICIPANT_ID
WHEN MATCHED THEN UPDATE SET SOME_DATA = S.SOME_DATA
WHEN NOT MATCHED THEN INSERT (INBOX_ID, PARTICIPANT_ID, SOME_DATA) 
VALUES (S.INBOX_ID, S.PARTICIPANT_ID, S.SOME_DATA);