web-dev-qa-db-ja.com

Spring 3での行挿入から自動生成キーを取得する/ PostgreSQL 8.4.9

行挿入から自動生成されたIDを取得したいのですが、NullPointerExceptionを取得します

コードは次のとおりです。

_long result = 0;
        final String SQL = "INSERT INTO compte (prenom, nom, datenaissance, numtelephone) "
                            + " VALUES(?,?,?,?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        int row= this.jdbcTemplate.update(new PreparedStatementCreator(){
            public PreparedStatement createPreparedStatement(Connection connection)
                throws SQLException {
                PreparedStatement ps =connection.prepareStatement(SQL);
                ps.setString(1, a.getSurname());
                ps.setString(2, a.getName());
                ps.setDate(3, a.getDob());
                ps.setString(4, a.getPhone());
                return ps;
            }
        },keyHolder);

        if (row > 0)
            result = keyHolder.getKey().longValue(); //line 72
_

そして、これはPostgreSQLのテーブルです:

_CREATE TABLE compte
(
  idcompte serial NOT NULL,
  prenom character varying(25) NOT NULL,
  nom character varying(25) NOT NULL,
  datenaissance date NOT NULL,
  numtelephone character varying(15) NOT NULL,
  CONSTRAINT pk_compte PRIMARY KEY (idcompte )
);
_

PostgreSQLは自動生成キーをサポートしていますが、この例外が発生します:

_Java.lang.NullPointerException
    at com.tante.db.JDBCUserAccountDAO.insertAccount(JDBCUserAccountDAO.Java:72)
_

編集:私はこれを試して、自動生成されたキーを取得しました:

_result = jdbcTemplate.queryForLong("select currval('compte_idcompte_seq')");
_

しかし、私はPSQLExceptionを取得します:

the current value (currval) of the sequence compte_idcompte_seq is not defined in this session、行を挿入するときに_compte_idcompte_seq.NEXTVAL_を呼び出すべきだと思っていたが

編集:

行が挿入されると、自動インクリメント値が適切に作成されます

何か案が ?

44
Jerec TheSith
KeyHolder holder = new GeneratedKeyHolder();

getJdbcTemplate().update(new PreparedStatementCreator() {           

                @Override
                public PreparedStatement createPreparedStatement(Connection connection)
                        throws SQLException {
                    PreparedStatement ps = connection.prepareStatement(sql.toString(),
                        Statement.RETURN_GENERATED_KEYS); 
                    ps.setString(1, person.getUsername());
                    ps.setString(2, person.getPassword());
                    ps.setString(3, person.getEmail());
                    ps.setLong(4, person.getRole().getId());
                    return ps;
                }
            }, holder);

Long newPersonId = holder.getKey().longValue();

Postgresの新しいバージョンでは、使用する必要があることに注意してください

connection.prepareStatement(sql.toString(), 
    new String[] { "idcompte" /* name of your id column */ })

の代わりに

connection.prepareStatement(sql.toString(), 
    Statement.RETURN_GENERATED_KEYS);
59
Artur

Spring JDBCでINSERTからキーを取得する最も簡単な方法は、SimpleJdbcInsertクラスを使用することです。 Spring Reference Guideの SimpleJdbcInsertを使用して自動生成キーを取得する というタイトルのセクションに例を見ることができます。

22
Paul

私はSpring3.1 + PostgreSQL9.1を使用していますが、これを使用するとき

    KeyHolder keyHolder = new GeneratedKeyHolder();
    jdbcTemplate.update(new PreparedStatementCreator() {
        public PreparedStatement createPreparedStatement(Connection connection)
                throws SQLException {
            PreparedStatement ps = 
                connection.prepareStatement(youSQL, 
                    Statement.RETURN_GENERATED_KEYS);
            ps.setString(1, post.name_author);
            ...
            return ps;
        }
    }, keyHolder);
    long id = keyHolder.getKey().longValue();

私はこの例外を受け取りました:

 org.springframework.dao.InvalidDataAccessApiUsageException: 
The getKey method should only be used when a single key is returned.  
The current key entry contains multiple keys: ...

だから私はに変更しました:

PreparedStatement ps = 
connection.prepareStatement(youSQL, new String[]{"id"});

「id」は

id serial not null primary key

そして問題は解決されました。だから私は

prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

ここではありません。公式ガイドは here 、13.2.8章:自動生成キーの取得

12
xKommando

Sequence.nextvalでNamedParameterJdbcTemplateを使用するソリューション:

        MapSqlParameterSource parameters = new MapSqlParameterSource();
        parameters.addValue("prenom", prenom);
        parameters.addValue("nom", nom);
        parameters.addValue("datenaissance", datenaissance);
        parameters.addValue("numtelephone", numtelephone);

    final String SQL = "INSERT INTO compte (idcompte,  prenom, nom, datenaissance, numtelephone) "
                        + " VALUES(compte_idcompte_seq.NEXTVAL, :prenom, :nom, :datenaissance, :numtelephone)";

        KeyHolder keyHolder = new GeneratedKeyHolder();
        NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(txManager.getDataSource());
        int nb = namedJdbcTemplate.update(SQL, parameters, keyHolder, new String[]{"idcompte"});
        Long generatedId = keyHolder.getKey().longValue();

NamedParameterJdbcTemplateを使用すると、特に大きなクエリがある場合に、パラメーターが名前で渡され、コードが読みやすくエラーが発生しにくいため、このソリューションが気に入っています。

5
Vasile Bors

キーホルダーとPostgreSQLにはいくつかの既知の問題があるようです。このリンクの回避策をご覧ください Spring JDBC-Last Inserted id

また、データベーステーブルを直接チェックして、レコードが適切に挿入されているかどうかを確認します(PKを使用)。これは問題を絞り込むのに役立ちます。

1
Kshitij

この投稿、特にINSERT ... RETURNING構文を使用することで受け入れられている答えをご覧ください。

最後に挿入された行から値を取得する方法?

これはPostgreSQLでこの値を取得するための最良の方法です。ネットワークラウンドトリップを1つだけ実行し、サーバー上で単一のアトミック操作として実行されるためです。

1
Matthew Wood

私は同じ問題を抱えていましたが、テーブルIDが自動インクリメントされていませんでした。

1
Eidan Spiegel

同様の問題に直面しました。この問題に正確に直面した理由はわかりませんが、以下のコードを使用してこれを解決するのは良いことです:

final KeyHolder holder = new GeneratedKeyHolder();
int status = myTemplate.update(yourInsertSQL, namedParameters, holder, new String[]{"PrimaryKeyColumnName"});

それが誰かを助けることを願っています。

1
AmitG
setGeneratedKeysColumnNames(new String[]{"column_name"});

自動生成された列の名前を設定することを忘れないでください。

0
N. Osil