Como fazer vários inserts Java/Postgres, passando o ID anterior como parâmetro o próximo?

Boa tarde.

Fazendo meu trabalho da faculdade, me deparei com mais uma dúvida!!

Como fazer vários inserts no banco de dados Postgres (acredito que não apenas para o Postgres, mas enfim…) passando o ID do insert anterior como parâmetro para o próximo insert na FK, mas que, possa garantir a integridade o insert como se tivesse passando um insert de uma vez só.

Na verdade, os inserts eu consigui fazer, o problema foi que não consegui manter a integridade!

Meu professor de banco de dados, disse que em banco “ou tudo é feito ou nada é feito”, ou seja, se eu passar um script no banco para criar tabelas, inserts, etc… Se em algum ponto tiver algum problema, nada é “comitado”.

Realmente, fiz testes no PgAdmin e isso acontece mesmo. Até antes de criar o sql para minha classe em java, eu fazia o teste no PgAdmin.

E problema que estou tendo é:

Ao fazer um insert em uma tabela, preciso pegar o id desse insert para passar para o próximo insert. Isso eu fiz com o getGeneratedKeys() de ResultSet.

No entanto, o problema é: se o segundo insert tiver um problema o primeiro também não deveria ser comitado, mas no meu caso esta sendo!!

Eu fiz duas tabelas, uma para nome de usário e nível de acesso e outra para login e senha. Meu professor disse para fazer login e senha separado, que era o ideal por questões de segurança, não fiz encriptação de senha, apenas separei as tabelas.

Mas aconteceu que, comecei a fazer os inserts pela tabela de nome e quando ia fazer o segundo insert e encontrava um login já existente (login = unique), dava erro. Até aí tudo bem, era para dar mesmo, mas não era para comitar nada, mas a primeira tabela foi inserida e a segunda não.

Resolvi isso invertendo a ordem dos inserts.

Mas, esse problema posso encontro também quando se tenta fazer um insert de um dado nulo em um not null, se for um segundo insert, o primeiro já foi inserido também.

Abaixo o método que fiz para fazer os inserts dos nomes e logins:

package model.dao;

import java.sql.*;
import java.util.*;
import java.util.logging.*;
import model.Connection_BD;
import model.bean.Usuario;
import view._util.Tools_SQL;

public class UsuarioDAO {

    private static Usuario userReturn = null;

    //==========================================================================
    // cria (cadastra) um novo registro
    public static int create(Usuario user) {

        Connection con = Connection_BD.getConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        String sql = null;
        int returnId = -1;

        try {

            //------------------------------------------------------------------
            // Primeiro INSERT - Tabela acessos (tabela de login e senha)
            sql = "INSERT INTO acessos(login, senha) VALUES (?, ?)";
            stmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

            stmt.setString(1, user.getLogin());
            stmt.setString(2, user.getSenha());

            stmt.executeUpdate();
            rs = stmt.getGeneratedKeys(); // recupera a key gerada no insert

            // pega o "ID" do cadastro atual
            if (rs.next()) {
                returnId = rs.getInt("id_acesso");
            }

            //------------------------------------------------------------------
            // Segundo INSERT - Tabela usuario
            sql = "INSERT INTO usuarios(id_usuario, nome, nivel_acesso) VALUES (?, ?, ?)";
            stmt = con.prepareStatement(sql);

            stmt.setInt(1, returnId);
            stmt.setString(2, user.getNome());
            stmt.setString(3, user.getNivelAcesso());

            stmt.executeUpdate();

            return returnId; // retorna o id para a aplicação

        } catch (SQLException ex) {
            Tools_SQL.mensagemErro(ex); // exibe mensagem de erro, caso ocorra
        } finally {
            Connection_BD.closeConnection(con, stmt, rs); // encerra conexão
        }

        return 0;
    }
	
	/*
	.....
	*/
}

Como posso garantir a integridade e não comitar ao fazer vários inserts?

Att.

Não é só isso. Faltou dizer que você precisa delimitar a transação. O comportamento default é uma transação por instrução. Se você quer que um conjunto de operações seja feito atomicamente, deve incluir todos em uma única transação. Se algo der errado, a transação inteira é desfeita e nada acontece no banco.

https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html

Obrigado, lvbarbosa.

Vou fazer umas alterações e testar.

Att.

Faça você mesmo o controle de transação, mude sua classe de conexão com o postgres de auto commit para auto commit off, aí vai funcionar da maneira que o @lvbarbosa disse, se tudo funcionar a transação é concluída (aplica o commit), caso contrário é desfeita por completo (aplica o rollback).

Exemplo:

private static Connection conexaoPostgreSQL; //este é o objeto utilizado para a configuração

conexaoPostgreSQL.setAutoCommit(false); //definindo o auto commit como false

Se a idéia é se preocupar com segurança, a preocupaçao mínima tem que ser armazenar um hash ao invés da senha em texto puro. Qualquer outra coisa que fizer antes disso nao resolve o problema.

pode fazer isso com suporte a transações, set auto commint , roolback etc… olhe este projeto
em java, com suporte a JDBC , commit, roolback, suporte a transações etc. esta separado as camadas, bem didático e de fácil compreensão.

quanto a segurança, armazenar o hash da senha, persistir o hash etc, é valido, mas acho ideia aqui é o relacionamento
das tabelas entre si, integridade das informações etc. segurança das informações persistidas é outra questão.