Como persistir dados de objetos distintos enviados pelo mesmo formulário com JSF?

28/10/2010

Tenho um formulario de cadastro de hospedes com 2 objetos a serem persistidos no banco de dados. Criei um formulário com a seguinte estrutura: Dados do hospede Nome: CPF: RG:   Dados veículo Placa : Cor: Marca: Modelo:   Tenho um managedBean chamado chamado HospedeBean com o método ...., responsavel por persistir somente os dados referentes a hopede. Em outro managedBean tenho um texto responsavel por cadastrar Carros.   Qual a melhor forma de persistir estas informações no banco de dados.   Segue imagem do MER de carros chamado merCarros
Marcos Sousa

Marcos Sousa

Curtidas 0

Respostas

Henrique Weissmann

Henrique Weissmann

28/10/2010

Oi Marcos, se é para persistir dados em duas tabelas diferentes, usando o mesmo formulário, o que você pode fazer é usar o padrão wrapper na classe do seu Managed Bean, tal como no exemplo de código abaixo:


class ManagedBeanWrapper {
private Hospede hospede;
private Carro carro;
// Crie getters e setters que referenciem atributos dos atributos internos, tal como no exeplo abaixo:
public String getNomeHospede() {return getHospede().getNome();}
public void setNomeHospede(String valor) {getHospede().setNome(valor;)}

}

Quanto ao salvamento, na action que for persistir os dados, basta tratá-los individualmente.
GOSTEI 0
Marcos Sousa

Marcos Sousa

28/10/2010

Henrique em minha classe HospedeBean criei os getters and setters da forma que voce me orientou deixando a estrutura como um Wrapper.

Mais na hora de persistir os dados estou com problemas, ele me retorna um sucesso na operação mais ao consultar minha base de dados os unicos dados que são gravados na base são de hospede os dados referentes ao Carro não são persitidos. Veja como criei a logica para salvar os dados

 public String cadastrar() throws DAOException {

        if (hospede.equals(hospede)) {
            if (hospede.getId() != null && hospede.getId().intValue() > 0) {

                hospedeDAOImpl.alterarEntidade(hospede);
                return "sucessoAlteraHospede";

            } else {

                hospedeDAOImpl.salvarEntidade(hospede);

                return "sucessoCadastraHospede";
            }
        } else{
            if(carro.getId() !=null && carro.getId().intValue() > 0){
                carroDAOimpl.alterarEntidade(carro);
                return "sucessoAlteraCarro";
            } else{
                carroDAOimpl.salvarEntidade(carro);
                return "sucessoCadastraCarro";
            }
        }
    }

Envio o link para aceso a classe HospedeBean: http://paste-it.net/public/z85f298/
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

28/10/2010

Oi Marcos,

com base apenas na classe HospedeBean, não há como saber exatamente o porquê dos dados não estarem ainda presentes na sua base de dados, mas com base na minha experiência, posso te apontar duas possibilidades.

* O Hibernate não envia os comandos imediatamente para o servidor de bancos de dados - por padrão, o Hibernate só envia os comandos SQL para a base de dados em lote. Isto é feito ou após algum período de tempo (que pode variar de acordo com a versão do Hibernate) ou quando um número x de comandos está acumulado na sessão.
Solução: execute o método flush da sua session. Isto informa o Hibernate que deve enviar os comandos para a base de dados naquele momento.

* Alguma excessão está ocorrendo durante o processo de persistência e está sendo ignorada. Observe se no código fonte do seu DAO há algum trecho que apresente um padrão similar ao abaixo exposto:

try {
   // tudo o que deve ser executado, no caso, a persistência dos dados
} catch (Exception ex) {
  // bloco vazio
}

Se o bloco estiver vazio, o código funciona normalmente, sem que a excessão seja detectada. Dica: ao invés de usar Exception, use Throwable. Throwable detecta qualquer problema, inclusive as excessões de execução.
GOSTEI 0
Marcos Sousa

Marcos Sousa

28/10/2010

Bom dia Henrique. Fiz duas alterações na minha classe responsavel por realizar o CRUD chamada CrudDAOJPA<T> segue link para acesso: http://paste-it.net/public/k655635/

Nos metodos salvarEntidade e alterarEntidade alterei o catch de Exception para Throwable

Fiz um debug na aplicação e percebi que tenho um erro na lógica ao salvar os dados. Olhe o método da classe HospedeBean

 public String cadastrar() throws DAOException {

        if (hospede.equals(hospede)) {
            if (hospede.getId() != null && hospede.getId().intValue() > 0) {

                hospedeDAOImpl.alterarEntidade(hospede);
                return "sucessoAlteraHospede";

            } else {

                hospedeDAOImpl.salvarEntidade(hospede);

                return "sucessoCadastraHospede";
            }
        } else{
            if(carro.getId() !=null && carro.getId().intValue() > 0){
                carroDAOimpl.alterarEntidade(carro);
                return "sucessoAlteraCarro";
            } else{
                carroDAOimpl.salvarEntidade(carro);
                return "sucessoCadastraCarro";
            }
        }
    }

Ao executar o método ele pega o objeto hospede e o remete ao else interno que chama o metodo hospedeDAOImpl.salvarEntidade(hospede); Quando ele realiza esta operação ele já faz o returno de sucesso ao cadastrar o hospede e não efetua o cadastro do Carro.

Qual a sugestão para melhorar esta lógica?

Desde já agradeço.
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

28/10/2010

Oi Marcos, nestes casos, em que você tem duas opções, salvar um objeto ou editar os seus dados na base de dados, eu costumo proceder da seguinte forma: nas classes que representam entidades, faço com que todas sejam derivadas de um tipo comum, no qual implemento o atributo identificador como privado e também os seus métodos get e set, que serão herdados por todas as demais classes de domínio.

Nesta mesma classe, também implemento uma função que me retorne true ou false, dizendo se o registro já foi persitido na base de dados ou não. Depende muito do caso, mas normalmente é um teste baseado no atributo que identifica a chave primária, como por exemplo retornar true para campos primários do tipo inteiro ou longo que sejam maiores que zero, indicando se tratar de um registro já presente na base de dados.

Você pode reaproveitar esta característica nos seus managed beans, tal como no seu exemplo. Se você chamasse esta função antes de retornar a string que identifica a navegação, sua vida ficaria mais fácil.

Finalmente, nos DAOs, ao invés de ter um método para salvar e outro para editar, tenho um único chamado persistir. Este verifica o valor da função que me diz se estou lidando com um registro novo ou não e, de acordo com isto, chama o método apropriado do hibernate (save ou update).

Procedendo desta forma, você reduz bastante a complexidade do seu projeto e, consequentemente, a possibilidade de ocorrência de erros.
GOSTEI 0
Marcos Sousa

Marcos Sousa

28/10/2010

Voce poderia me enviar um exemplo com esta implementação?

Desde já agradeço.

GOSTEI 0
Henrique Weissmann

Henrique Weissmann

28/10/2010

Oi Marcos, posso sim.

Vamos usar o seu próprio exemplo ok? No caso, as entidades seriam Carro e Hospede. Nesta situação, eu criaria uma classe abstrata chamada Entidade, da qual ambas seriam subclasses cuja implementação seria basicamente a seguinte:

public abstract class Entidade {
     // Supondo que o identificador seja do tipo String, e eu use o algoritmo UUID para defini-lo
     private String id;
     public String getId() {return this.id;}
     public void setId(String valor) {this.id = valor;}

     /*
         Uma vez persistido, o próprio Hibernate irá gerar o valor do atributo ID para mim usando este
         algoritmo. Se não tiver ainda sido gerado, é sinal de que ainda não foi inserido na base de dados
     */
     public String isPersistido() {
         return getId() != null;
     }

      /*
           Estou usando o tipo genérico de excessão apenas para simplificar o exemplo
      */
      public void persistir() throws Exception {
             if (isPersistido()) {
                     updateBD();
             } else {
                      incluirBD();
             }
      }

      public void updateBD() throws Exception {
             // edição no banco de dados entra aqui
       }

       public void incluirBD() throws Exception {
            // inserção no banco de dados entra aqui
       }

}
GOSTEI 0
Marcos Sousa

Marcos Sousa

28/10/2010

Boa noite Henrique. Estive fazendo uma implementação de teste para verificar a usabilidade de seu exemplo. Criei a seguinte estrutura no projeto.

Criei um pacote chamado exemplo.entity composto pelas classes:
Entidade: http://paste-it.net/public/a02926e/;HospedeTeste: http://paste-it.net/public/e369f9d/;CarroTeste: http://paste-it.net/public/af8725f/Somente em CarroTeste inclui o extends Entidade e ele me apresenta a sugestão de criar o ID para a entidade. Na Entidade tenho os seguintes erros:

public Integer isPersistido(){
        return getId() != null; //tipo de retorno incompativel ele requer boolean e recebe Integer.
    }

A implementação será desta forma mesmo, o que devo fazer para corrigir estas falhas?

Desde já agradeço.

GOSTEI 0
Henrique Weissmann

Henrique Weissmann

28/10/2010

Oi Marcos,

no caso, seu erro está no tipo declarado de retorno e o valor que você está retornando.

Repare:

public Integer isPersistido(){
        return getId() != null; //tipo de retorno incompativel ele requer boolean e recebe Integer.
}

seu método isPersistido, é do tipo Integer. Mas em seu interior, você está retornando o resultado de uma operação de comparação que, em Java, sempre é um valor booleano.

Reescreva-a da seguinte maneira:

public boolean isPersistido() {
       return getId() > 0;
}

Por que isto: supondo que seja um campo autoincremental, o primeiro registro na tabela sempre será 1, e zero quando ainda não foi persistido.
GOSTEI 0
Devmedia

Devmedia

28/10/2010

Marcos,a resposta solucionou o seu problema? podemos encerrar o chamado?
GOSTEI 0
Marcos Sousa

Marcos Sousa

28/10/2010

Peço um prazo, pois não tive tempo de realizar as correções sugeridas pelo Henrique.

GOSTEI 0
Devmedia

Devmedia

28/10/2010

Marcos,
por falta de retorno estamos encerrando o chamado. Caso volte a ter dúvidas sobre o assunto aqui tratado, por favor, volte a postar aqui mesmo e o consultor voltará a lhe atender.
GOSTEI 0
POSTAR