6

データベースにレコードを挿入しようとしている次のコードがあります。

try {
 Connection conn = getConnection();

 String sql = 
   "INSERT INTO myTable(userId,content,timestamp) VALUES(?,?,NOW())";
 PreparedStatement st = 
    conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);

 st.setLong(1, userId);
 st.setString(2, content);
 id = st.executeUpdate(); //this is the problem line            
} catch(Exception e) {}

問題は、レコードが正しく挿入されているにもかかわらず、挿入idされたばかりのレコードの主キー+auto_incrementIDを含めたいことです。ただし、何らかの理由で、IDとして常に「1」が返されます。これuserIdは、挿入中の値が1であるためと考えられます。

私のテーブルはInnoDBです。最初userIdは別のテーブルへの外部キーでしたが、その後、外部キーとuserId列のインデックスも削除しましたが、戻り値として1を取得しています。

私が間違っていることについて何か考えはありますか?

4

5 に答える 5

8

PreparedStatment.executeUpdate()

戻り値:
(1)SQLデータ操作言語(DML)ステートメントの行数または(2)何も返さないSQLステートメントの場合は0

execute()代わりに使用して、 ResultSetwithを取得する必要がありgetGeneratedKeys()ます。必要なデータが含まれます。

編集して追加:テーブルに自動インクリメントフィールドがないので、あなたの質問を読みました userId

于 2013-02-13T01:19:40.250 に答える
7

ブライアンローチによって受け入れられた答えは正しいです。私はいくつかの考えと完全なコードの例を追加しています。

RETURN_GENERATED_KEYS「生成されたキーを返す」という意味ではありません</ h1 >

元のポスターは、当然のことながら、旗の言い回しによって混乱しているようStatement.RETURN_GENERATED_KEYSです。直感に反して、このフラグを渡してもメソッドの動作は変わりません。PreparedStatement::executeUpdateこのメソッドは常にint、実行されたSQLの影響を受ける行数であるを返します。「executeUpdate」メソッドは、生成されたキーを返すことはありません。

int countRowsAffected = pstmt.executeUpdate();  // Always return number of rows affected, *not* the generated keys.

尋ねなさい、そうすればあなたがたは受け取るだろう

生成されたキーが必要な場合は、次の2つの手順を実行する必要があります。

  1. 旗を渡す、そして
  2. ResultSet生成されたキー値のみを含む行で構成されていることを要求します。

この配置により、他の望ましい動作を維持しながら、生成されたキーを取り戻す動作を追加して、影響を受ける行数のカウントを取得できます。

サンプルコード

これは、データフィードからデータを取得するJava8アプリから取得したほぼ実際の例です。この文脈では、本格的な例の方が最小限の例よりも役立つと思います。

細かい点…このコードは、構文上またはその他の方法で完全ではない可能性があります。これは、実際のソースコードをコピーして貼り付けて変更したためです。テーブルの代理主キーとして、整数ではなくUUIDデータ型を使用しています。クラスとは私自身のものであり、その詳細はここでは重要ではありません。と変数は、私自身のアプリの意味のあるデータの置き換えです。私のロギング呼び出しはSLF4Jフレームワークに対して行われます。UUID16進文字列は、ログ内のレポートを元のソースコードにリンクするための便利な方法です。データベースはPostgresですが、この種のコードは、生成されたキーのレポートをサポートするすべてのデータベースで機能するはずです。CharHelperDBHelperxy

public UUID dbWrite (  String x , String y , DateTime whenRetrievedArg ) {
    if ( whenRetrievedArg == null ) {
        logger.error( "Passed null for whenRetrievedArg. Message # 2112ed1a-4612-4d5d-8cc5-bf27087a350d." );
        return null;
    }

    Boolean rowInsertComplete = Boolean.FALSE; // Might be used for debugging or logging or some logic in other copy-pasted methods.

    String method = "Method 'dbWrite'";
    String message = "Insert row for some_table_ in " + method + ". Message # edbea872-d3ed-489c-94e8-106a8e3b58f7.";
    this.logger.trace( message );

    String tableName = "some_table_";

    java.sql.Timestamp tsWhenRetrieved = new java.sql.Timestamp( whenRetrievedArg.getMillis() );  // Convert Joda-Time DatTime object to a java.sql.Timestamp object.

    UUID uuidNew = null;

    StringBuilder sql = new StringBuilder( AbstractPersister.INITIAL_CAPACITY_OF_SQL_STRING ); // private final static Integer INITIAL_CAPACITY_OF_SQL_STRING = 1024;
    sql.append( "INSERT INTO " ).append( tableName ).append( CharHelper.CHAR.PAREN_OPEN_SPACED ).append( " x_ , y_ " ).append( CharHelper.CHAR.PAREN_CLOSED ).append( DBHelper.SQL_NEWLINE );
    sql.append( "VALUES ( ? , ? , ?  ) " ).append( DBHelper.SQL_NEWLINE );
    sql.append( ";" );

    try ( Connection conn = DBHelper.instance().dataSource().getConnection() ;

ここでステップ1RETURN_GENERATED_KEYSを実行し、フラグを渡します。

            PreparedStatement pstmt = conn.prepareStatement( sql.toString() , Statement.RETURN_GENERATED_KEYS ); ) {

引き続きステートメントの準備と実行を行います。int countRows = pstmt.executeUpdate();生成されたキーではなく、影響を受けた行の数を返すことに注意してください。

        pstmt.setString( 1 , x ); 
        pstmt.setString( 2 , y ); 
        pstmt.setTimestamp( 3 , tsWhenRetrieved );  
        // Execute
        int countRows = pstmt.executeUpdate();  // Always returns an int, a count of affected rows. Does *not* return the generated keys.
        if ( countRows == 0 ) {  // Bad.
            this.logger.error( "Insert into database for new " + tableName + " failed to affect any rows. Message # 67e8de7e-67a5-42a6-a4fc-06929211e6e3." );
        } else if ( countRows == 1 ) {  // Good.
            rowInsertComplete = Boolean.TRUE;
        } else if ( countRows > 1 ) {  // Bad.
            rowInsertComplete = Boolean.TRUE;
            this.logger.error( "Insert into database for new " + tableName + " failed, affecting more than one row. Should not be possible. Message # a366e215-6cf2-4e5c-8443-0b5d537cbd68." );
        } else { // Impossible.
            this.logger.error( "Should never reach this Case-Else with countRows value " + countRows + " Message # 48af80d4-6f50-4c52-8ea8-98856873f3bb." );
        }

ここでは、ステップ2を実行し、生成されたキーのResultSetを要求します。この例の場合、単一の行を挿入し、生成された単一のキーが返されることを期待しています。

        if ( rowInsertComplete ) {
            // Return new row’s primary key value.
            ResultSet genKeys = pstmt.getGeneratedKeys();
            if ( genKeys.next() ) {
                uuidNew = ( UUID ) genKeys.getObject( 1 );  // ResultSet should have exactly one column, the primary key of INSERT table.
            } else {
                logger.error( "Failed to get a generated key returned from database INSERT. Message # 6426843e-30b6-4237-b110-ec93faf7537d." );
            }
        }

残りはエラー処理とクリーンアップです。このコードの下部に、挿入されたレコードの生成された主キーであるUUIDが返されることに注意してください。

    } catch ( SQLException ex ) {
        // We expect to have occasional violations of unique constraint on this table in this data-scraping app.
        String sqlState = ex.getSQLState();
        if ( sqlState.equals( DBHelper.SQL_STATE.POSTGRES.UNIQUE_CONSTRAINT_VIOLATION ) ) {  // SqlState code '23505' = 'unique_violation'.
            this.logger.trace( "Found existing row when inserting a '" + tableName + "' row for y: " + y + ". Expected to happen on most attempts. Message # 0131e8aa-0bf6-4d19-b1b3-2ed9d333df27." );
            return null; // Bail out.
        } else { // Else any other exception, throw it.
            this.logger.error( "SQLException during: " + method + " for table: " + tableName + ", for y: " + y + ". Message # 67908d00-2a5f-4e4e-815c-5e5a480d614b.\n" + ex );
            return null; // Bail out.
        }
    } catch ( Exception ex ) {
        this.logger.error( "Exception during: " + method + " for table: " + tableName + ", for y: " + y + ". Message # eecc25d8-de38-458a-bb46-bd6f33117969.\n" + ex );
        return null;  // Bail out.
    }

    if ( uuidNew == null ) {
        logger.error( "Returning a null uuidNew var. SQL: {} \nMessage # 92e2374b-8095-4557-a4ed-291652c210ae." , sql );
    }
    return uuidNew;
}
于 2015-07-17T06:57:58.987 に答える
2
String SQLQuery=" ";

String generatedKeys[]= {"column_name"};//'column_name' auto-increment column

prepSt = Connection.prepareStatement(SQLQuery,generatedKeys);

prepSt.setInt(1, 1234); 

.....

.....

....


prepSt.executeUpdate();

ResultSet rs = prepSt.getGeneratedKeys; // used same PreparedStatement object as used   for Insert .


if(rs.next()) {


int id=rs.getLong("column_name");


System.out.println(id);


}
} catch (SQLException e) {
}
于 2014-04-27T22:04:09.917 に答える
1

データベースに自動インクリメントを設定している場合はuserId、自分で追加しようとしないでください。を挿入するNULLと、自動インクリメントされます。(手がかりは名前にあります!)

また、テーブルを更新するのではなく、テーブルに挿入します。したがって、executeUpdate()は実行しません。試す...

PreparedStatement pst = conn.prepareStatement("INSERT INTO myTable(userId,content,timestamp) VALUES(NULL,?,NOW())");
pst.setString(1, content);
pst.executeQuery();
于 2013-02-13T01:20:35.723 に答える
1

取得するのは、「Rowsinsetrted」(INSERTステートメントの場合)の通知です。このメソッドを使用して、DMLクエリが成功したかどうかを確認します。以下は、[prepareStatement(yourSQL、Statement.RETURN_GENERATED_KEYS)]を使用して自動生成されたIDを取得する方法です。このメソッドはRowID参照のみを返すことに注意してください。実際の値を取得するには、plsは方法2を参照します。

(方法1)

Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
myPrepStatement = <Connection>.prepareStatement(yourSQL, Statement.RETURN_GENERATED_KEYS);
myPrepStatement.setInt(1, 123); 
myPrepStatement.setInt(2, 123); 

myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
  java.sql.RowId rid=rs.getRowId(1); 
  //what you get is only a RowId ref, try make use of it anyway U could think of
  System.out.println(rid);
}
} catch (SQLException e) {
}

(方法2)

Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
//IMPORTANT: here's where other threads don tell U, you need to list ALL cols 
//mentioned in your query in the array
myPrepStatement = <Connection>.prepareStatement(yourSQL, new String[]{"Id","Col2","Col3"});
myPrepStatement.setInt(1, 123); 
myPrepStatement.setInt(2, 123); 
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
//In this exp, the autoKey val is in 1st col
  int id=rs.getLong(1);
  //now this's a real value of col Id
  System.out.println(id);
}
} catch (SQLException e) {
}

基本的に、SEQ.Nextvalの値が必要な場合は、Method1を使用しないでください。これは、RowID refを返すだけで、頭を割って使用する方法を見つけることができます。これは、キャストしようとしたすべてのデータ型にも適合しません。に!これは、MySQL、DB2では正常に機能する(実際の値を返す)場合がありますが、Oracleでは機能しません。

重要:デバッグ中は、SQL Developer、Toad、または同じログインセッションを使用してINSERTを実行するクライアントをオフにしてください。毎回影響を与えるとは限りません(デバッグ呼び出し)...しばらくの間、例外なくアプリがフリーズするまで。はい...例外なく停止します!

于 2013-06-24T08:09:00.797 に答える