JDBC'de ekleme kimliği nasıl alınır?


385

INSERTJava JDBC kullanarak bir veritabanında (benim durumumda Microsoft SQL Server olan) bir kayıt istiyorum . Aynı zamanda insert kimliğini almak istiyorum. JDBC API'sini kullanarak bunu nasıl başarabilirim?


GetGeneratedKeys () Sorgusunda AutoGenrerated kimliğini bırakın String sql = "INSERT INTO 'yash'.'mytable' ('name') VALUES (?)"; int primkey = 0 ; PreparedStatement pstmt = con.prepareStatement(sql, new String[] { "id" }/*Statement.RETURN_GENERATED_KEYS*/); pstmt.setString(1, name); if (pstmt.executeUpdate() > 0) { java.sql.ResultSet generatedKeys = pstmt.; if (generatedKeys.next()) primkey = generatedKeys.getInt(1); }
Yash

Yanıtlar:


650

Otomatik olarak oluşturulan bir anahtarsa, Statement#getGeneratedKeys()bunun için kullanabilirsiniz . Aynı üzerine demen gerekiyor Statementbiri için kullanılan olarak INSERT. İlk olarak , anahtarları döndürmesi için JDBC sürücüsünü bilgilendirmek için kullanarak deyimi oluşturmanız gerekirStatement.RETURN_GENERATED_KEYS .

İşte temel bir örnek:

public void create(User user) throws SQLException {
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_INSERT,
                                      Statement.RETURN_GENERATED_KEYS);
    ) {
        statement.setString(1, user.getName());
        statement.setString(2, user.getPassword());
        statement.setString(3, user.getEmail());
        // ...

        int affectedRows = statement.executeUpdate();

        if (affectedRows == 0) {
            throw new SQLException("Creating user failed, no rows affected.");
        }

        try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
            if (generatedKeys.next()) {
                user.setId(generatedKeys.getLong(1));
            }
            else {
                throw new SQLException("Creating user failed, no ID obtained.");
            }
        }
    }
}

JDBC sürücüsünün çalışıp çalışmadığına bağlı olduğunuza dikkat edin. Şu anda, son sürümlerin çoğu işe yarayacak, ancak doğruysam Oracle JDBC sürücüsü bu konuda hala biraz zahmetli. MySQL ve DB2 zaten yıllardır destekliyordu. PostgreSQL uzun zaman önce desteklemeye başladı. Daha önce hiç kullanmadığım için MSSQL hakkında yorum yapamam.

Oracle için, son oluşturulan anahtarı elde etmek üzere aynı işlemden hemen sonra CallableStatementbir RETURNINGcümle veya bir SELECT CURRVAL(sequencename)(veya DB'ye özgü herhangi bir sözdizimi) içeren bir çağrı çağırabilirsiniz INSERT. Ayrıca bu cevaba bakınız .


4
Eklemeden önce bir sonraki değeri, eklemeden sonra akım almaktan daha iyidir, çünkü ikincisi çok iş parçacıklı bir ortamda (örneğin, herhangi bir web uygulaması kabı) yanlış değeri döndürebilir. JTDS MSSQL sürücüsü getGeneratedKeys'i destekler.
JeeBee

4
(genellikle Oracle kullandığımı açıklığa kavuşturmalı, bu yüzden normalde bir JDBC sürücüsünün yeteneklerinden çok düşük beklentilere sahibim).
JeeBee

7
Statement.RETURN_GENERATED_KEYS seçeneğinin ayarlanmamasının ilginç bir yan etkisi, tamamen belirsiz olan hata iletisidir. "Herhangi bir sonuç alınmadan önce ifade çalıştırılmalıdır."
Chris Winters

7
generatedKeys.next()Döner trueDB eğer oluşturulmuş bir anahtar döndü. Bak, bu bir ResultSet. close()Bedava kaynaklara etmektir. Aksi takdirde DB'niz uzun vadede tükenir ve uygulamanız bozulur. Sadece kapanış görevini yapan bazı yarar yöntemlerini kendiniz yazmanız gerekir. Ayrıca bu ve bu cevaba bakınız.
BalusC

5
Çoğu veritabanı / sürücü için doğru cevap. Ancak Oracle için bu çalışmaz. Oracle için şunu değiştirin: connection.prepareStatement (sql, new String [] {"PK sütun adı"});
Darrell Teague

24
  1. Oluşturulan Sütun Oluştur

    String generatedColumns[] = { "ID" };
  2. Bu oluşturulmuş Sütunu ifadenize iletin

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. ResultSetDeyimde GeneratedKeys getirmek için nesne kullanın

    ResultSet rs = stmtInsert.getGeneratedKeys();
    
    if (rs.next()) {
        long id = rs.getLong(1);
        System.out.println("Inserted ID -" + id); // display inserted record
    }
    

8

Microsoft SQL Server 2008 R2 tek iş parçacıklı JDBC tabanlı bir uygulamadan vuruyorum ve RETURN_GENERATED_KEYS özelliğini veya herhangi bir PreparedStatement kullanmadan son kimliği geri çekiyorum. Şuna benzer bir şey var:

private int insertQueryReturnInt(String SQLQy) {
    ResultSet generatedKeys = null;
    int generatedKey = -1;

    try {
        Statement statement = conn.createStatement();
        statement.execute(SQLQy);
    } catch (Exception e) {
        errorDescription = "Failed to insert SQL query: " + SQLQy + "( " + e.toString() + ")";
        return -1;
    }

    try {
        generatedKey = Integer.parseInt(readOneValue("SELECT @@IDENTITY"));
    } catch (Exception e) {
        errorDescription = "Failed to get ID of just-inserted SQL query: " + SQLQy + "( " + e.toString() + ")";
        return -1;
    }

    return generatedKey;
} 

Bu blog gönderisi üç ana SQL Server "last ID" seçeneğini güzel bir şekilde izole ediyor: http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / - diğer ikisine henüz ihtiyaç duymadım.


4
Uygulamanın yalnızca bir iş parçacığı olması bir yarış koşulunu imkansız hale getirmez: iki istemci bir satır ekler ve yönteminizle kimliği alırsa başarısız olabilir.
11684

Neden ki? Ben sadece birden fazla iş parçacığı izin verirken kod hatalarını ayıklamak zorunda yoksul sod değil sevindim!
mjaggard

6

Kullanırken bir 'Desteklenmeyen özellik' hatasıyla karşılaştığınızda şunu Statement.RETURN_GENERATED_KEYSdeneyin:

String[] returnId = { "BATCHID" };
String sql = "INSERT INTO BATCH (BATCHNAME) VALUES ('aaaaaaa')";
PreparedStatement statement = connection.prepareStatement(sql, returnId);
int affectedRows = statement.executeUpdate();

if (affectedRows == 0) {
    throw new SQLException("Creating user failed, no rows affected.");
}

try (ResultSet rs = statement.getGeneratedKeys()) {
    if (rs.next()) {
        System.out.println(rs.getInt(1));
    }
    rs.close();
}

BATCHIDOtomatik oluşturulan kimlik nerede .


demek istedinizBATCHID
MoolsBytheway

Mükemmel cevap!!!
Hasitha Jayawardana

3

SQLServer 2008 kullanıyorum , ancak bir geliştirme sınırlaması var: Bunun için yeni bir sürücü kullanamıyorum, "com.microsoft.jdbc.sqlserver.SQLServerDriver" ("kullanamam" com.microsoft.sqlserver.jdbc kullanmak zorundayım) .SQLServerDriver ").

Bu yüzden çözüm benim için conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)bir java.lang.AbstractMethodError attı . Bu durumda, bulduğum olası bir çözüm Microsoft tarafından önerilen eski çözümdür: JDBC Kullanarak @@ IDENTITY Değerini Alma

import java.sql.*; 
import java.io.*; 

public class IdentitySample
{
    public static void main(String args[])
    {
        try
        {
            String URL = "jdbc:microsoft:sqlserver://yourServer:1433;databasename=pubs";
            String userName = "yourUser";
            String password = "yourPassword";

            System.out.println( "Trying to connect to: " + URL); 

            //Register JDBC Driver
            Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();

            //Connect to SQL Server
            Connection con = null;
            con = DriverManager.getConnection(URL,userName,password);
            System.out.println("Successfully connected to server"); 

            //Create statement and Execute using either a stored procecure or batch statement
            CallableStatement callstmt = null;

            callstmt = con.prepareCall("INSERT INTO myIdentTable (col2) VALUES (?);SELECT @@IDENTITY");
            callstmt.setString(1, "testInputBatch");
            System.out.println("Batch statement successfully executed"); 
            callstmt.execute();

            int iUpdCount = callstmt.getUpdateCount();
            boolean bMoreResults = true;
            ResultSet rs = null;
            int myIdentVal = -1; //to store the @@IDENTITY

            //While there are still more results or update counts
            //available, continue processing resultsets
            while (bMoreResults || iUpdCount!=-1)
            {           
                //NOTE: in order for output parameters to be available,
                //all resultsets must be processed

                rs = callstmt.getResultSet();                   

                //if rs is not null, we know we can get the results from the SELECT @@IDENTITY
                if (rs != null)
                {
                    rs.next();
                    myIdentVal = rs.getInt(1);
                }                   

                //Do something with the results here (not shown)

                //get the next resultset, if there is one
                //this call also implicitly closes the previously obtained ResultSet
                bMoreResults = callstmt.getMoreResults();
                iUpdCount = callstmt.getUpdateCount();
            }

            System.out.println( "@@IDENTITY is: " + myIdentVal);        

            //Close statement and connection 
            callstmt.close();
            con.close();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        try
        {
            System.out.println("Press any key to quit...");
            System.in.read();
        }
        catch (Exception e)
        {
        }
    }
}

Bu çözüm benim için çalıştı!

Umarım bu yardımcı olur!


1

Bir yorum yerine, sadece gönderiyi cevaplamak istiyorum.


Java.sql.PreparedStatement arabirimi

  1. columnIndexes «columnIndexes ve SQL deyimini kabul eden preparStatement işlevini kullanabilirsiniz. ColumnIndexes tarafından izin verilen sabit bayraklar Statement.RETURN_GENERATED_KEYS 1 veya Statement.NO_GENERATED_KEYS [2], bir veya daha fazla '' içerebilecek SQL ifadesi '?' IN parametre yer tutucuları.

    SYNTAX «

    Connection.prepareStatement(String sql, int autoGeneratedKeys)
    Connection.prepareStatement(String sql, int[] columnIndexes)

    Misal:

    PreparedStatement pstmt = 
        conn.prepareStatement( insertSQL, Statement.RETURN_GENERATED_KEYS );

  1. columnNames « Sütun adını benzer şekilde listeleyin 'id', 'uniqueID', .... Hedef tabloda, döndürülmesi gereken otomatik oluşturulan anahtarları içerir. SQL deyimi bir INSERTdeyim değilse sürücü bunları yoksayar .

    SYNTAX «

    Connection.prepareStatement(String sql, String[] columnNames)

    Misal:

    String columnNames[] = new String[] { "id" };
    PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );

Tam Örnek:

public static void insertAutoIncrement_SQL(String UserName, String Language, String Message) {
    String DB_URL = "jdbc:mysql://localhost:3306/test", DB_User = "root", DB_Password = "";

    String insertSQL = "INSERT INTO `unicodeinfo`( `UserName`, `Language`, `Message`) VALUES (?,?,?)";
            //"INSERT INTO `unicodeinfo`(`id`, `UserName`, `Language`, `Message`) VALUES (?,?,?,?)";
    int primkey = 0 ;
    try {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        Connection conn = DriverManager.getConnection(DB_URL, DB_User, DB_Password);

        String columnNames[] = new String[] { "id" };

        PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
        pstmt.setString(1, UserName );
        pstmt.setString(2, Language );
        pstmt.setString(3, Message );

        if (pstmt.executeUpdate() > 0) {
            // Retrieves any auto-generated keys created as a result of executing this Statement object
            java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();
            if ( generatedKeys.next() ) {
                primkey = generatedKeys.getInt(1);
            }
        }
        System.out.println("Record updated with id = "+primkey);
    } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
}

Bu çözümü çok iş parçacıklı çalışma zamanı ortamında kullanmak güvenli midir?
Prototip

1

Yeni eklenen kimliği almak için aşağıdaki java kodunu kullanabilirsiniz.

ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, quizid);
ps.setInt(2, userid);
ps.executeUpdate();

ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
    lastInsertId = rs.getInt(1);
}

0

Hibernate'in NativeQuery ile, Hibernate yerel bir sorguyu değiştirdiğinden, SingleResult yerine ResultList döndürmeniz gerekir

INSERT INTO bla (a,b) VALUES (2,3) RETURNING id

sevmek

INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1

tek bir sonuç almaya çalışırsanız, çoğu veritabanının (en azından PostgreSQL) bir sözdizimi hatası atmasına neden olur. Daha sonra, elde edilen kimliği listeden alabilirsiniz (genellikle tam olarak bir öğe içerir).


0

Normal ile de kullanmak mümkündür Statement(sadece PreparedStatement)

Statement statement = conn.createStatement();
int updateCount = statement.executeUpdate("insert into x...)", Statement.RETURN_GENERATED_KEYS);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
  if (generatedKeys.next()) {
    return generatedKeys.getLong(1);
  }
  else {
    throw new SQLException("Creating failed, no ID obtained.");
  }
}

0

Benim durumumda ->

ConnectionClass objConnectionClass=new ConnectionClass();
con=objConnectionClass.getDataBaseConnection();
pstmtGetAdd=con.prepareStatement(SQL_INSERT_ADDRESS_QUERY,Statement.RETURN_GENERATED_KEYS);
pstmtGetAdd.setString(1, objRegisterVO.getAddress());
pstmtGetAdd.setInt(2, Integer.parseInt(objRegisterVO.getCityId()));
int addId=pstmtGetAdd.executeUpdate();              
if(addId>0)
{
    ResultSet rsVal=pstmtGetAdd.getGeneratedKeys();
    rsVal.next();
    addId=rsVal.getInt(1);
}

Yine de bunu elde etmenin uzun bir yaklaşım olduğunu düşünüyorum. Bence daha sıkıştırılmış bir çözüm de olacak.
TheSagya


-6
Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret  = st.execute();

Affedersiniz, ama bunun ne olması gerekiyor?
MoolsBytheway

1. createStatementyöntemi Connectionherhangi bir parametre beklemeyin. 2. executeden gelen yöntem Statementbir Sorgu ile bir Dize bekler. 3. executeYöntem döndürür: trueilk sonuç bir ResultSetnesne ise; falsegüncelleme sayısı ise veya sonuç yoksa. docs.oracle.com/javase/7/docs/api/java/sql/…
atilacamurca
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.