该声明已中止,因为它会导致重复键

时间:2016-05-24 10:25:35

标签: java netbeans prepared-statement derby embedded-database

我一直收到这个错误:

Error code 20000, SQL state 23505
Insert command failed: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL160524112023610' defined on 'TEST'.

enter image description here

当我运行此代码时:

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DerbyBatch {

    private static Connection connection;

    public static void main(String args[]) {
        try {
            createDatabase();
            createTable();
            insertBatch();
        } catch (ClassNotFoundException | SQLException ex) {
            Logger.getLogger(DerbyBatch.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void createDatabase() throws SQLException {
        connection = DriverManager.getConnection("jdbc:derby:" + new File("test").getAbsolutePath() + ";" + "create=true");
        disconnect();
    }

    public static void createTable() throws ClassNotFoundException, SQLException {
        connect();
        String createTable = "CREATE TABLE \"APP\".\"TEST\" (ID INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), TEXT VARCHAR (30000) NOT NULL, PRIMARY KEY (ID))";
        PreparedStatement preparedStatement = connection.prepareStatement(createTable);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        connection.commit();
        disconnect();
    }

    public static void insertBatch() throws SQLException, ClassNotFoundException {
        connect();
        String sql = "INSERT INTO TEST (ID, TEXT) VALUES (?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1, 1);
        preparedStatement.setString(2, "TEST");
        preparedStatement.addBatch();
        preparedStatement.executeBatch();
        connection.commit();
        disconnect();
        Logger.getLogger(DerbyBatch.class.getName()).log(Level.SEVERE, "All data inserted.");
    }

    public static void connect() throws ClassNotFoundException, SQLException {
        Class.forName("net.sf.log4jdbc.DriverSpy");
        Connection temp = DriverManager.getConnection("jdbc:log4jdbc:derby:" + new File("test").getAbsolutePath());
        connection = new net.sf.log4jdbc.ConnectionSpy(temp);
        connection.setAutoCommit(false);
    }

    public static void disconnect() throws SQLException {
        connection.close();
    }
}

然后尝试在netbeans服务标签中手动插入记录 - >数据库:

enter image description here

我确保所有内容都已提交并且连接已正确关闭,因此我不确定为什么德比会在执行批量插入后继续拾取重复索引?

2 个答案:

答案 0 :(得分:4)

关于GENERATED BY DEFAULT,假设自动生成的值应该从最后一个现有ID开始是错误的;它们总是从START WITH值开始。

因此,当您尝试使用NetBeans插入另一行时,将首次触发自动ID生成,并且它将尝试插入具有START WITH值的ID;因为这是1,就像您通过Java手动输入的ID一样,它会因重复键错误而失败。

official documentation也涵盖了您的具体情况:

create table greetings 
        (i int generated by default as identity, ch char(50));
-- specify value "1":
insert into greetings values (1, 'hi');
-- use generated default 
insert into greetings values (DEFAULT, 'salut');
-- use generated default 
insert into greetings(ch) values ('bonjour');  
     

请注意,与GENERATED ALWAYS列不同,GENERATED BY DEFAULT   列不保证唯一性。

     因此,在上面的例子中,   hisalut行的标识值均为1,因为。{   生成的列从1开始,用户指定的值也是   1

     

防止重复,尤其是在加载或导入时   数据,使用对应的START WITH值创建表   系统应分配的第一个标识值。

因此,您要么不手动插入ID而只使用自动生成,要么相应地将START WITH更改为手动插入的ID,例如START WITH 2

您还可以使用START WITH ALTER TABLE更改RESTART WITH值。 documentation of ALTER TABLE有一个例子可以解决与你类似的情况:

  

考虑以下示例,其中涉及到的组合   自动生成的数据和手动插入的数据:

CREATE TABLE tauto(i INT GENERATED BY DEFAULT AS IDENTITY, k INT)
CREATE UNIQUE INDEX tautoInd ON tauto(i)
INSERT INTO tauto(k) values 1,2
     

系统   将自动为标识列生成值。但现在   您需要手动将一些数据插入标识列:

INSERT INTO tauto VALUES (3,3)
INSERT INTO tauto VALUES (4,4)
INSERT INTO tauto VALUES (5,5)
     

标识列使用了15的值   这点。如果您现在希望系统生成一个值,那么系统   将生成3,这将导致唯一的密钥异常,因为   值3已经手动插入。

     

为了弥补   手动插入,为标识列发出ALTER TABLE语句   与RESTART WITH 6

ALTER TABLE tauto ALTER COLUMN i RESTART WITH 6

答案 1 :(得分:0)

从我所看到的情况来看,您的测试只能成功运行一次。之后,除非您在测试后删除创建的表,否则它将在重复的插入,重复的表创建或类似的东西上失败。