web-dev-qa-db-fra.com

Obtenir la clé générée automatiquement à partir de l'insertion de ligne au printemps 3/PostgreSQL 8.4.9

Je voudrais récupérer l'id généré automatiquement à partir d'une insertion de ligne, mais j'obtiens un NullPointerException

Voici le code:

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

Et voici la table 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 supporte les clés générées automatiquement, mais j'obtiens cette exception:

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

EDIT: J'ai essayé ceci pour obtenir la clé générée automatiquement:

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

mais je reçois une PSQLException

the current value (currval) of the sequence compte_idcompte_seq is not defined in this session, bien que je pensais que compte_idcompte_seq.NEXTVAL.__ aurait dû être appelé lors de l'insertion de la ligne

MODIFIER : 

La valeur d'auto-incrémentation est correctement créée lorsqu'une ligne est insérée

Une idée ?

43
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();

Notez que dans les nouvelles versions de Postgres, vous devez utiliser 

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

au lieu de 

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

Le moyen le plus simple de récupérer une clé à partir d'un INSERT avec Spring JDBC consiste à utiliser la classe SimpleJdbcInsert. Vous pouvez voir un exemple dans le manuel Spring Reference Guide, dans la section intitulée Récupération de clés générées automatiquement à l'aide de SimpleJdbcInsert .

22
Paul

J'utilise Spring3.1 + PostgreSQL9.1, et quand j'utilise ce

    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();

J'ai cette exception:

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

Alors j'ai changé pour:

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

où "id" est

id serial not null primary key

Et le problème est résolu. Alors j'ai supposé qu'en utilisant 

prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

n'est pas juste ici. Le guide officiel est ici , Chapitre 13.2.8: Récupération des clés générées automatiquement

10
xKommando

Une solution utilisant NamedParameterJdbcTemplate avec Sequence.nextval: 

        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();

J'aime cette solution, car avec NamedParameterJdbcTemplate, les paramètres sont passés par nom et le code est plus lisible et moins sujet aux erreurs, en particulier lorsqu'il y a de grandes requêtes.

5
Vasile Bors

Il semble y avoir des problèmes connus avec Keyholder et PostgreSQL. Regardez la solution de contournement à ce lien Spring JDBC - Dernier inséré id

Vérifiez également la table de base de données directement pour voir si l’enregistrement est inséré correctement (c.-à-d. Avec PK). Cela aidera à réduire le problème.

1
Kshitij

Jetez un coup d'œil à ce billet, en particulier à la réponse acceptée en utilisant la syntaxe INSERT ... RETURNING:

Comment obtenir une valeur de la dernière ligne insérée?

C’est le meilleur moyen d’obtenir cette valeur dans PostgreSQL, car vous n’effectuez qu’un seul aller-retour sur le réseau, le tout en une seule opération atomique sur le serveur.

1
Matthew Wood

J'ai eu le même problème et il s'est avéré que mon identifiant de table n'était pas incrémenté automatiquement.

1
Eidan Spiegel

J'ai fait face au même problème. Je ne sais pas pourquoi je suis confronté à ce problème, mais je dois le résoudre en utilisant le code ci-dessous:

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

J'espère que ça aide quelqu'un.

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

N'oubliez pas de définir les noms des colonnes générées automatiquement.

0
N. Osil