PreparedStatement con Statement.RETURN_GENERATED_KEYS

83

La única forma en que algunos controladores JDBC regresan Statement.RETURN_GENERATED_KEYSes hacer algo de lo siguiente:

long key = -1L;
Statement statement = connection.createStatement();
statement.executeUpdate(YOUR_SQL_HERE, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = statement.getGeneratedKeys();
if (rs != null && rs.next()) {
    key = rs.getLong(1);
}

¿Hay alguna forma de hacer lo mismo con PreparedStatement?


Editar

La razón por la que pregunté si puedo hacer lo mismo con PreparedStatementconsiderar el siguiente escenario:

private static final String SQL_CREATE = 
            "INSERT INTO
            USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS, DOB) 
            VALUES (?, ?, ?, ?, ?)";

En la USERtabla hay un PRIMARY KEY (USER_ID)que es un BIGINT AUTOINCREMENT(por lo tanto, por qué no lo ve en el SQL_CREATEString.

Ahora, completo el ?using PreparedStatement.setXXXX(index, value). Quiero volver ResultSet rs = PreparedStatement.getGeneratedKeys(). ¿Cómo puedo conseguir esto?

Buhake Sindi
fuente
2
Muchas personas entienden mal y usan PreparedStatement # executeUpdate (arg). El documento de Java dice que This method with argument cannot be called on a PreparedStatement or CallableStatement.significa que tenemos que usar executeUpdate () sin argumentos, aunque el executeUpdate(arg)método se puede heredar en la clase PreparedStatement, pero no tenemos que usarlo, de lo contrario obtendremos SQLException.
AmitG

Respuestas:

141

Puede usar el prepareStatementmétodo tomando un intparámetro adicional

PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)

Para algunos controladores JDBC (por ejemplo, Oracle), debe enumerar explícitamente los nombres de columna o los índices de las claves generadas:

PreparedStatement ps = con.prepareStatement(sql, new String[]{"USER_ID"})
Jörn Horstmann
fuente
Acepté su respuesta porque mostró más formas de lograr el mismo resultado.
Buhake Sindi
67

¿Te refieres a algo como esto?

long key = -1L;

PreparedStatement preparedStatement = connection.prepareStatement(YOUR_SQL_HERE, PreparedStatement.RETURN_GENERATED_KEYS);
preparedStatement.setXXX(index, VALUE);
preparedStatement.executeUpdate();

ResultSet rs = preparedStatement.getGeneratedKeys();

if (rs.next()) {
    key = rs.getLong(1);
}
nanda
fuente
¿Cómo puede ser nulo el conjunto de resultados de claves generadas?
AlikElzin-kilaka
10

Al no tener un compilador para mí en este momento, responderé haciendo una pregunta:

¿Has probado esto? ¿Funciona?

long key = -1L;
PreparedStatement statement = connection.prepareStatement();
statement.executeUpdate(YOUR_SQL_HERE, PreparedStatement.RETURN_GENERATED_KEYS);
ResultSet rs = statement.getGeneratedKeys();
if (rs != null && rs.next()) {
    key = rs.getLong(1);
}

Descargo de responsabilidad: obviamente, no he compilado esto, pero entiendes la idea.

PreparedStatement es una subinterfaz de Statement , por lo que no veo ninguna razón por la que esto no funcione, a menos que algunos controladores JDBC tengan errores.

darioo
fuente
eso no es lo que estoy buscando. Sé que PreparedStatementes una subclase de Statement.... ver mi publicación actualizada.
Buhake Sindi
2
String query = "INSERT INTO ....";
PreparedStatement preparedStatement = connection.prepareStatement(query, PreparedStatement.RETURN_GENERATED_KEYS);

preparedStatement.setXXX(1, VALUE); 
preparedStatement.setXXX(2, VALUE); 
....
preparedStatement.executeUpdate();  

ResultSet rs = preparedStatement.getGeneratedKeys();  
int key = rs.next() ? rs.getInt(1) : 0;

if(key!=0){
    System.out.println("Generated key="+key);
}
Dharmendrasinh Chudasama
fuente
Si se genera la clave, la clave de lo contrario es clave = 0 si no se genera
Dharmendrasinh Chudasama
0
private void alarmEventInsert(DriveDetail driveDetail, String vehicleRegNo, int organizationId) {

    final String ALARM_EVENT_INS_SQL = "INSERT INTO alarm_event (event_code,param1,param2,org_id,created_time) VALUES (?,?,?,?,?)";
    CachedConnection conn = JDatabaseManager.getConnection();
    PreparedStatement ps = null;
    ResultSet generatedKeys = null;
    try {
        ps = conn.prepareStatement(ALARM_EVENT_INS_SQL, ps.RETURN_GENERATED_KEYS);
        ps.setInt(1, driveDetail.getEventCode());
        ps.setString(2, vehicleRegNo);
        ps.setString(3, null);
        ps.setInt(4, organizationId);
        ps.setString(5, driveDetail.getCreateTime());
        ps.execute();
        generatedKeys = ps.getGeneratedKeys();
        if (generatedKeys.next()) {
            driveDetail.setStopDuration(generatedKeys.getInt(1));
        }
    } catch (SQLException e) {
        e.printStackTrace();
        logger.error("Error inserting into alarm_event : {}", e
                .getMessage());
        logger.info(ps.toString());
    } finally {
        if (ps != null) {
            try {

                if (ps != null)
                    ps.close();
            } catch (SQLException e) {
                logger.error("Error closing prepared statements : {}", e
                        .getMessage());
            }
        }
    }
    JDatabaseManager.freeConnection(conn);
}
niraj
fuente
1
¿No debería liberar su conexión en el bloque final, no fuera de él (obtendrá una conexión si obtiene una excepción de tiempo de ejecución de cualquier tipo)?
Jules
@niraj - en lugar de ps.RETURN_GENERATED_KEYS podemos escribir Statement.RETURN_GENERATED_KEYS porque es una variable estática en la clase java.sql.Statement.
AmitG