Pessoal, estou com um probleminha, tenho que iniciar implementação de um sistema e o mesmo possui como regra, que qualquer inserção, alteração ou exclusão deve ser registrado na base, claro que devem saber que isso se chama Trilha de auditoria. A minha pergunta é! Existe um tutorial ou algum material na Web, na qual eu possa dar início a produção da trilha de auditoria em java? Basta me apontarem o caminho.
Se você tiver usando JPA/Hibernate procure na documentação sobre Listeners/Interceptors. São os lugares ideiais pra esse tipo de lógica.
E
eude.lacerda
Sim,vou aplicar JPA/Hibernate.
Obrigado!
Será que teria um exemplo bem simples?
R
rodrigo.uchoa
Na própria documentação do Hibernate, para explicar Interceptors, eu acho que ele usa um exemplo exatamente de auditoria. Mas colocando no google também não deve ser difícil de encontrar.
Tive que implementar isso aqui e utilizei o módulo do Hibernate chamado de Hibenrate Envers. Ele já cria a tabela de auditoria para você e persite toda alteração lá. Você não precisará ter que implementar nada na mão. [=
R
rodrigo.uchoa
Opa, tudo bem?
Tive que implementar isso aqui e utilizei o módulo do Hibernate chamado de Hibenrate Envers. Ele já cria a tabela de auditoria para você e persite toda alteração lá. Você não precisará ter que implementar nada na mão. [=
É. Como o Hebert disse, o Envers funciona bem. Acho que inclusive nas versões mais novas ele já vem embutido no “core” do Hibernate. Sö tomaria cuidado se pra você não seria “matar formiguinha com bala de canhão”. O Envers duplica todas as tabelas que você deseja auditar e tudo mais.
R
rodrigo.uchoa
Alias, minha sugestão mesmo seria você tentar fazer isso a nível de banco.
H
Hebert_Coelho
rodrigo.uchoa:
Opa, tudo bem?
Tive que implementar isso aqui e utilizei o módulo do Hibernate chamado de Hibenrate Envers. Ele já cria a tabela de auditoria para você e persite toda alteração lá. Você não precisará ter que implementar nada na mão. [=
É. Como o Hebert disse, o Envers funciona bem. Acho que inclusive nas versões mais novas ele já vem embutido no “core” do Hibernate. Sö tomaria cuidado se pra você não seria “matar formiguinha com bala de canhão”. O Envers duplica todas as tabelas que você deseja auditar e tudo mais.
Na verdade ele está totalmente configurável agora. Você pode escolher quais tabelas, colunas serão salvos. Ou seja, apenas as informações que você desejar serão salvas.
Gosto dele pois você mantém o controle da auditoria que é onde está a regra de negócio.
Por mim, é apenas mais uma solução para auditoria. Espero que ele escolha o que for melhor para o sistema dele. [=
I
Ironlynx
eude.lacerda,
é interessante também conversar com o cliente para ver o que ele quer de verdade.Já tive que fazer um sistema com documentos em que NADA era realmente apagado.A diferença estava do que cada user/role podia ver no sistema. A marcação de deletar apenas deixava NÃO visível aquele documento.
E
eude.lacerda
Hebert,
O módulo Envers é aplicável com a especificação JPA? Pois, é o que vou usar, juntamente com um provedor Hibernate.
rodrigo.uchoa,
Mas será que o sistema não ficaria dependente do SGBD que vou usar, caso no futuro, o cliente queira mudar de banco?
R
rodrigo.uchoa
Hebert,
O módulo Envers é aplicável com a especificação JPA? Pois, é o que vou usar, juntamente com um provedor Hibernate.
Responder pelo Hebert aqui
Sim, dá pra usar sem problemas. É só declarar os listeners no persistence.xml.
rodrigo.uchoa,
Mas será que o sistema não ficaria dependente do SGBD que vou usar, caso no futuro, o cliente queira mudar de banco?
Pois é, essa é uma questão que depende muito da sua realidade ai. O SGBD tem a clara vantagem que se houver intervenções diretamente no banco, a auditoria também será feita, e também é bem mais rápido. Por outro lado, você vai ser obrigado a manter código de banco também.
H
Hebert_Coelho
rodrigo.uchoa:
Hebert,
O módulo Envers é aplicável com a especificação JPA? Pois, é o que vou usar, juntamente com um provedor Hibernate.
Responder pelo Hebert aqui
Sim, dá pra usar sem problemas. É só declarar os listeners no persistence.xml.
Não precisa de listener mais. [=
A única configuração que coloquei (mas não é obrigatória) foi apenas para mudar o nome da tabela de auditoria.
<property name=“org.hibernate.envers.audit_table_suffix” value="_AUDITORIA"/>
Creio que vale a pena você dar uma olhada nele agora, tá ponta firme.
H
Hebert_Coelho
eude.lacerda:
Hebert,
O módulo Envers é aplicável com a especificação JPA? Pois, é o que vou usar, juntamente com um provedor Hibernate.
rodrigo.uchoa,
Mas será que o sistema não ficaria dependente do SGBD que vou usar, caso no futuro, o cliente queira mudar de banco?
Sim, ele é do Hibernate. [=
E
eude.lacerda
Hebert Coelho:
Opa, tudo bem?
Tive que implementar isso aqui e utilizei o módulo do Hibernate chamado de Hibenrate Envers. Ele já cria a tabela de auditoria para você e persite toda alteração lá. Você não precisará ter que implementar nada na mão. [=
Hebert,
Como faço para fazer o envers registra todas as alterações de todas entidades numa única entidade? É isso que o líder do projeto quer.
Obrigado.
H
Hebert_Coelho
eude.lacerda:
Hebert Coelho:
Opa, tudo bem?
Tive que implementar isso aqui e utilizei o módulo do Hibernate chamado de Hibenrate Envers. Ele já cria a tabela de auditoria para você e persite toda alteração lá. Você não precisará ter que implementar nada na mão. [=
Hebert,
Como faço para fazer o envers registra todas as alterações de todas entidades numa única entidade? É isso que o líder do projeto quer.
Obrigado.
Não sei se é possível e para te falar a verdade, eu diria que não seria uma boa prática pois essa tabela viraria um monstro de colunas com valor null e não null, de difícil manutenção e péssima performance.
Tive que implementar isso aqui e utilizei o módulo do Hibernate chamado de Hibenrate Envers. Ele já cria a tabela de auditoria para você e persite toda alteração lá. Você não precisará ter que implementar nada na mão. [=
Hebert,
Como faço para fazer o envers registra todas as alterações de todas entidades numa única entidade? É isso que o líder do projeto quer.
Obrigado.
Não sei se é possível e para te falar a verdade, eu diria que não seria uma boa prática pois essa tabela viraria um monstro de colunas com valor null e não null, de difícil manutenção e péssima performance.
Acho que você não me entendeu, não será salvar todos os campos de todas as entidades, mas criar um tabela padrão onde possa, salvar: o id do registro, nome da tabela, tipo de operação realizada, campo alterado, valor antigo e novo valor. Será que é possível? Eu olhei alguns tutoriais e percebi que pode se criar uma entidade de revisão. Será que esse conceito pode ser aplicado com aquilo que estou querendo. Entendi muito bem o Hibernate Envers, será que é aplicável a minha necessidade?
Obrigado.
H
Hebert_Coelho
eude.lacerda:
Hebert,
Acho que você não me entendeu, não será salvar todos os campos de todas as entidades, mas criar um tabela padrão onde possa, salvar: o id do registro, nome da tabela, tipo de operação realizada, campo alterado, valor antigo e novo valor. Será que é possível? Eu olhei alguns tutoriais e percebi que pode se criar uma entidade de revisão. Será que esse conceito pode ser aplicado com aquilo que estou querendo. Entendi muito bem o Hibernate Envers, será que é aplicável a minha necessidade?
Obrigado.
Nesse caso não sei.
Sempre utilizei o envers com pouca customização.
A
Ataxexe1 like
Provavelmente isso não é possível com o Envers porque nem é o escopo dele. O Envers serve para auditar entidades e não operações no banco de dados. Inclusive, você pode fazer queries com o entity manager dele para buscar uma entidade em um determinado instante de tempo, montar a entidade dessa forma seria muito complexo se o Envers auditasse operações no banco em vez da entidade como um todo.
Se você realmente precisa fazer a auditoria no banco de dados, o Envers não será de muita utilidade.
E
eude.lacerda
Provavelmente isso não é possível com o Envers porque nem é o escopo dele. O Envers serve para auditar entidades e não operações no banco de dados. Inclusive, você pode fazer queries com o entity manager dele para buscar uma entidade em um determinado instante de tempo, montar a entidade dessa forma seria muito complexo se o Envers auditasse operações no banco em vez da entidade como um todo.
Se você realmente precisa fazer a auditoria no banco de dados, o Envers não será de muita utilidade.
Ataxexe,
Bem, o que você me sugere? Fazer auditoria criando mais uma tabela para cada uma existente, fica exagerada, em questão de tamanho, minha base de dados, pois, é isso que faz o Hibernate Envers. No meu caso são, por alto, 90 entidades, imagina.
Obrigado.
I
Impossivel
Ironlynx:
eude.lacerda,
é interessante também conversar com o cliente para ver o que ele quer de verdade.Já tive que fazer um sistema com documentos em que NADA era realmente apagado.A diferença estava do que cada user/role podia ver no sistema. A marcação de deletar apenas deixava NÃO visível aquele documento.
Melhor resposta que ficou obfuscada só porque não citou nenhum framework da moda.
A
Ataxexe
Provavelmente isso não é possível com o Envers porque nem é o escopo dele. O Envers serve para auditar entidades e não operações no banco de dados. Inclusive, você pode fazer queries com o entity manager dele para buscar uma entidade em um determinado instante de tempo, montar a entidade dessa forma seria muito complexo se o Envers auditasse operações no banco em vez da entidade como um todo.
Se você realmente precisa fazer a auditoria no banco de dados, o Envers não será de muita utilidade.
Ataxexe,
Bem, o que você me sugere? Fazer auditoria criando mais uma tabela para cada uma existente, fica exagerada, em questão de tamanho, minha base de dados, pois, é isso que faz o Hibernate Envers. No meu caso são, por alto, 90 entidades, imagina.
Obrigado.
Mas a auditoria em uma única tabela também não é nada leve e, em alguns casos, produz até mais registros (quando são alterados muitos dados de uma entidade, por exemplo). Você também pode colocar as tabelas de auditoria em outro schema, limitar as colunas auditáveis. Mas isso só será uma solução para você se o alvo da auditoria for a entidade, se o alvo da auditoria for a coluna de uma tabela, nem tente usar o Envers.
Em termos de espaço, você decide se quer ter uma tabela com milhões de registro ou algumas tabelas com milhares. É uma conta que deve ser feita com cuidado porque pode favorecer qualquer um dos mecanismos de auditoria.
Em termos de praticidade, flexibilidade e, o mais importante, visualização, auditar a entidade é melhor. Por exemplo, como você faria para consultar os dados de uma entidade há 3 meses? Auditando a entidade é muito simples, auditando as colunas não seria tão simples assim.
O problema é você querer forçar a barra usando auditoria de entidades se não é isso que vai resolver o problema do cliente. Quando tem DBAs no meio, geralmente a auditoria de entidades vai pelo ralo (e quase sempre por motivos fúteis).
E
eude.lacerda
Bom dia, Pessoal,
Bom, encontrei um um código do Carlos Sotolani, que por sinal muito bem produzido, resolveu minha necessidade última de uma única entidade receber todas as alterações. Apresentei ao líder do projeto, e ele concordou. Então o que fiz, estudei o código e tentei adaptá-lo ao que precisava, funcionou perfeitamente, vejam o que fiz.
Classe: AuditListener
publicclassAuditListener{publicstaticenumTipoOperacao{INCLUSAO,ATUALIZACAO,EXCLUSAO,}@PostPersistpublicvoidpostPersist(Objectobject){Stringid=null;// Busca pelo idField[]fields=object.getClass().getDeclaredFields();if(fields!=null&&fields.length>0){for(Fieldfield:fields){if(field.isAnnotationPresent(Id.class)){field.setAccessible(true);try{id=field.get(object).toString();}catch(Exceptione){return;}break;}}}// Fimif(id!=null){Auditoriaaudit=newAuditoria();audit.setNomeEntidade(object.getClass().getSimpleName());audit.setIdEntidade(Integer.parseInt(id));audit.setOperacao(String.valueOf(TipoOperacao.INCLUSAO));audit.setUsuario("CARLOS");audit.setData(newDate());audit.setAlteracao("");try{salvarAuditoria(audit);}catch(Exceptione){e.printStackTrace();return;}}}@PreUpdatepublicvoidpreUpdate(Objectobject){Objectid=null;// Busca pelo idField[]fields=object.getClass().getDeclaredFields();if(fields!=null&&fields.length>0){for(Fieldfield:fields){if(field.isAnnotationPresent(Id.class)){field.setAccessible(true);try{id=field.get(object);}catch(Exceptione){return;}}}}// FimObjectobjectOld=null;try{objectOld=buscarObjetoPorIdNovaTransacao(object.getClass(),id);}catch(Exceptione1){return;}Stringalteracao="";if(objectOld!=null){Field[]fieldsOld=objectOld.getClass().getDeclaredFields();if(fields!=null&&fields.length>0&&fieldsOld!=null&&fieldsOld.length>0){for(Fieldfield:fields){field.setAccessible(true);ObjectfieldValue=null;try{fieldValue=field.get(object);}catch(Exceptione){return;}for(FieldfieldOld:fieldsOld){if(field.getName().equals(fieldOld.getName())){fieldOld.setAccessible(true);ObjectfieldValueOld=null;try{fieldValueOld=fieldOld.get(objectOld);}catch(Exceptione){return;}if(fieldValueinstanceofString||fieldValueinstanceofInteger||fieldValueinstanceofDate){if(fieldValue==null&&fieldValueOld==null){}elseif(fieldValue==null&&fieldValueOld!=null){alteracao+=field.getName()+": "+fieldValueOld+" para null";}elseif(fieldValue!=null&&fieldValueOld==null){alteracao+=field.getName()+": null para "+fieldValue;}elseif(!fieldValue.equals(fieldValueOld)){alteracao+=field.getName()+": "+fieldValueOld+" para "+fieldValue;}}elseif(fieldValueinstanceofList){if(fieldValue==null&&fieldValueOld==null){}elseif(fieldValue==null&&fieldValueOld!=null){alteracao+=field.getName()+": "+((List<?>)fieldValueOld).size()+" para null";}elseif(fieldValue!=null&&fieldValueOld==null){alteracao+=field.getName()+": null para "+((List<?>)fieldValue).size();}elseif(!fieldValue.equals(fieldValueOld)){alteracao+=field.getName()+": "+((List<?>)fieldValueOld).size()+" para "+((List<?>)fieldValue).size();}}}}}}}if(!alteracao.equals("")){Auditoriaaudit=newAuditoria();audit.setNomeEntidade(object.getClass().getSimpleName());audit.setIdEntidade(Integer.parseInt(id.toString()));audit.setOperacao(String.valueOf(TipoOperacao.ATUALIZACAO));audit.setUsuario("CARLOS");audit.setData(newDate());audit.setAlteracao(alteracao);try{salvarAuditoria(audit);}catch(Exceptione){return;}}}@PostRemovepublicvoidpostRemove(Objectobject){if(object!=null){Stringid=null;// Busca pelo idField[]fields=object.getClass().getDeclaredFields();if(fields!=null&&fields.length>0){for(Fieldfield:fields){if(field.isAnnotationPresent(Id.class)){field.setAccessible(true);try{id=field.get(object).toString();}catch(Exceptione){return;}break;}}}// Fimif(id!=null){Auditoriaaudit=newAuditoria();audit.setNomeEntidade(object.getClass().getSimpleName());audit.setIdEntidade(Integer.parseInt(id));audit.setOperacao(String.valueOf(TipoOperacao.EXCLUSAO));audit.setUsuario("CARLOS");audit.setData(newDate());audit.setAlteracao("");try{salvarAuditoria(audit);}catch(Exceptione){e.getMessage();return;}}}}publicvoidsalvarAuditoria(Auditoriaaudit){EntityManagerem=JPAUtil.getEntityManager();em.getTransaction().begin();em.persist(audit);em.getTransaction().commit();}publicObjectbuscarObjetoPorIdNovaTransacao(Class<?>classes,Objectobject){returnJPAUtil.getEntityManager().find(classes,object);}}
Entidade: Auditoria
@Entity@XmlRootElement@NamedQueries({@NamedQuery(name="Auditoria.buscaTodos",query="SELECT f FROM Auditoria f")})publicclassAuditoriaimplementsSerializable{privatestaticfinallongserialVersionUID=1L;@Id@SequenceGenerator(name="SEQ_AUDITORIA",sequenceName="SEQ_ID_AUDITORIA",initialValue=1,allocationSize=1)@GeneratedValue(generator="SEQ_AUDITORIA",strategy=GenerationType.SEQUENCE)privateLongid;privateStringnomeEntidade;privateintidEntidade;privateStringoperacao;@Temporal(javax.persistence.TemporalType.TIMESTAMP)privateDatedata;@Column()privateStringalteracao;privateStringusuario;publicLonggetId(){returnid;}publicvoidsetId(Longid){this.id=id;}@OverridepublicinthashCode(){inthash=0;hash+=(id!=null?id.hashCode():0);returnhash;}@Overridepublicbooleanequals(Objectobject){// TODO: Warning - this method won't work in the case the id fields are not setif(!(objectinstanceofAuditoria)){returnfalse;}Auditoriaother=(Auditoria)object;if((this.id==null&&other.id!=null)||(this.id!=null&&!this.id.equals(other.id))){returnfalse;}returntrue;}@OverridepublicStringtoString(){return"entidades.Auditoria[ id="+id+" ]";}publicStringgetNomeEntidade(){returnnomeEntidade;}publicvoidsetNomeEntidade(StringnomeEntidade){this.nomeEntidade=nomeEntidade;}publicintgetIdEntidade(){returnidEntidade;}publicvoidsetIdEntidade(intidEntidade){this.idEntidade=idEntidade;}publicStringgetOperacao(){returnoperacao;}publicvoidsetOperacao(Stringoperacao){this.operacao=operacao;}publicDategetData(){returndata;}publicvoidsetData(Datedata){this.data=data;}publicStringgetAlteracao(){returnalteracao;}publicvoidsetAlteracao(Stringalteracao){this.alteracao=alteracao;}publicStringgetUsuario(){returnusuario;}publicvoidsetUsuario(Stringusuario){this.usuario=usuario;}}
E mágica acontece aqui, somente é inserida a anotação @EntityListener chamando AuditListener.class na entidade Pessoa.
@Entity@Table(name="Pessoa")@EntityListeners({AuditListener.class})@XmlRootElement@NamedQueries({@NamedQuery(name="Pessoa.buscaTodos",query="SELECT f FROM Pessoa f")})publicclassPessoaimplementsSerializable{privatestaticfinallongserialVersionUID=1L;@Id@SequenceGenerator(name="SEQ_PESSOA",sequenceName="SEQ_ID_PESSOA",initialValue=1,allocationSize=1)@GeneratedValue(generator="SEQ_PESSOA",strategy=GenerationType.SEQUENCE)privateLongid;@Column(name="nome",length=40)privateStringnome;@Column(length=11)privateStringcpf;publicLonggetId(){returnid;}publicvoidsetId(Longid){this.id=id;}@OverridepublicinthashCode(){inthash=0;hash+=(id!=null?id.hashCode():0);returnhash;}@Overridepublicbooleanequals(Objectobject){// TODO: Warning - this method won't work in the case the id fields are not setif(!(objectinstanceofPessoa)){returnfalse;}Pessoaother=(Pessoa)object;if((this.id==null&&other.id!=null)||(this.id!=null&&!this.id.equals(other.id))){returnfalse;}returntrue;}@OverridepublicStringtoString(){return"br.com.teste.entidades.Pessoa[ id="+id+" ]";}publicStringgetNome(){returnnome;}publicvoidsetNome(Stringnome){this.nome=nome;}publicStringgetCpf(){returncpf;}publicvoidsetCpf(Stringcpf){this.cpf=cpf;}}
Algumas vezes nem tudo que já vem pronto atende nossas necessidades.
Pessoal, obrigado pelas sugestões. Considero este post com fechado.
J
javaflex
Se atualmente não existe previsão real para mudança de banco, não invente, use trigger. Usar bola de cristal só traz complexidade ou obscuridade.
Geralmente ADs que devem cuidar disso, usando ferramentas para gerar e manter esses scripts.
Se é para considerar previsões por somente “se acontecer”, se quiser prever também que outro sistema poderá acessar esse banco, a solução via hibernate estará furada, onde qualquer mexida por fora do sistema, até mesmo por invasão, não será logada, o que até é mais preocupante do que mudança de banco não prevista, que terá investimento quando necessário.
A
Ataxexe
javaflex:
Se atualmente não existe previsão real para mudança de banco, não invente, use trigger. Usar bola de cristal só traz complexidade ou obscuridade.
Geralmente ADs que devem cuidar disso, usando ferramentas para gerar e manter esses scripts.
Se é para considerar previsões por somente “se acontecer”, se quiser prever também que outro sistema poderá acessar esse banco, a solução via hibernate estará furada, onde qualquer mexida por fora do sistema, até mesmo por invasão, não será logada, o que até é mais preocupante do que mudança de banco não prevista, que terá investimento quando necessário.
Concordo. Existem tantos pontos de falha e de baixo desempenho no código acima que é mais simples dar manutenção em uma trigger do que nele.
Se quiser usar realmente os interceptors, é bom dar um banho de loja no código pra ele não se tornar um vilão no desempenho do sistema a na manutenção.
Alguns exemplos em uma rápida analisada no código:
Reflection usada a torto e a direito em uma entidade sendo que poderia ser feito um cache na primeira vez
Somente campos declarados como atributos podem ser auditados
Apenas o uso de anotações em atributos é suportado (eu prefiro, mas vai que alguém coloca as anotações em getters, já que a especificação permite…)
Terrível uso de instanceof e inúmeros ifs tornam o código muito propenso a ser deixado de lado quando houver problemas
Péssimo tratamento de erros (o famoso PokemonExceptionHandler, pega todas as exceções e guarda na sua “pokebola” pra ninguém mais ver o que aconteceu)
Talvez o esforço necessário para criar um código enxuto e flexível para o seu caso seja mais caro do que usar uma solução direto no banco de dados, é importante colocar isso na ponta do lápis.
J
javaflex
Ataxexe:
javaflex:
Se atualmente não existe previsão real para mudança de banco, não invente, use trigger. Usar bola de cristal só traz complexidade ou obscuridade.
Geralmente ADs que devem cuidar disso, usando ferramentas para gerar e manter esses scripts.
Se é para considerar previsões por somente “se acontecer”, se quiser prever também que outro sistema poderá acessar esse banco, a solução via hibernate estará furada, onde qualquer mexida por fora do sistema, até mesmo por invasão, não será logada, o que até é mais preocupante do que mudança de banco não prevista, que terá investimento quando necessário.
Concordo. Existem tantos pontos de falha e de baixo desempenho no código acima que é mais simples dar manutenção em uma trigger do que nele.
Se quiser usar realmente os interceptors, é bom dar um banho de loja no código pra ele não se tornar um vilão no desempenho do sistema a na manutenção.
Alguns exemplos em uma rápida analisada no código:
Reflection usada a torto e a direito em uma entidade sendo que poderia ser feito um cache na primeira vez
Somente campos declarados como atributos podem ser auditados
Apenas o uso de anotações em atributos é suportado (eu prefiro, mas vai que alguém coloca as anotações em getters, já que a especificação permite…)
Terrível uso de instanceof e inúmeros ifs tornam o código muito propenso a ser deixado de lado quando houver problemas
Péssimo tratamento de erros (o famoso PokemonExceptionHandler, pega todas as exceções e guarda na sua “pokebola” pra ninguém mais ver o que aconteceu)
Talvez o esforço necessário para criar um código enxuto e flexível para o seu caso seja mais caro do que usar uma solução direto no banco de dados, é importante colocar isso na ponta do lápis.
Ótimas observações. Seguir o que é mais usado pelos responsáveis da área específica é sempre menos arriscado.
H
Hebert_Coelho
Ataxexe:
Concordo. Existem tantos pontos de falha e de baixo desempenho no código acima que é mais simples dar manutenção em uma trigger do que nele.
Se quiser usar realmente os interceptors, é bom dar um banho de loja no código pra ele não se tornar um vilão no desempenho do sistema a na manutenção.
Alguns exemplos em uma rápida analisada no código:
Reflection usada a torto e a direito em uma entidade sendo que poderia ser feito um cache na primeira vez
Somente campos declarados como atributos podem ser auditados
Apenas o uso de anotações em atributos é suportado (eu prefiro, mas vai que alguém coloca as anotações em getters, já que a especificação permite…)
Terrível uso de instanceof e inúmeros ifs tornam o código muito propenso a ser deixado de lado quando houver problemas
Péssimo tratamento de erros (o famoso PokemonExceptionHandler, pega todas as exceções e guarda na sua “pokebola” pra ninguém mais ver o que aconteceu)
Talvez o esforço necessário para criar um código enxuto e flexível para o seu caso seja mais caro do que usar uma solução direto no banco de dados, é importante colocar isso na ponta do lápis.
A única coisa que eu pensei ao ver o código foi: “coitado de quem der manutenção”.
Isso para mim é o famoso código bomba…
E
eude.lacerda
Boa tarde, pessoal,
Vejo que estão criticando código que foi postado, como solução para meu problema, peço a vocês que apresentem um solução para tal. Vim aqui com o intuito de pedir ajuda, quando apresentei uma solução para compartilhar com todos, criticam aquilo que postei. Acho que me enganei com este fórum.
Obrigado.
A
Ataxexe1 like
eude.lacerda:
Boa tarde, pessoal,
Vejo que estão criticando código que foi postado, como solução para meu problema, peço a vocês que apresentem um solução para tal. Vim aqui com o intuito de pedir ajuda, quando apresentei uma solução para compartilhar com todos, criticam aquilo que postei. Acho que me enganei com este fórum.
Obrigado.
Você não se enganou não. Fórum não é local de passar a mão na cabeça.
Eu listei vários problemas que podem ser usados pra solucionar seu código, se eu gastar um tempo corrigindo ele estarei trabalhando de graça pra você, como eu não posso fazer isso ainda (preciso trabalhar para a empresa que me contratou) achei melhor colocar os pontos falhos do código na esperança de que você pudesse corrigí-los e pedir ajudas pontuais em caso de dúvidas.
Mas parece que você quer que a gente ajude concordando com você, não é? Quando as pessoas dizem que está lindo não precisam dizer o motivo e tudo está bem, agora, se alguém diz que está feio e mostra onde está feio é tratado como se não quisesse ajudar…vai entender.
Nós demos uma solução viável (as triggers) e eu apontei problemas no código que devem ser cuidadosamente revistos caso você queira usar os interceptors (está lá na minha mensagem, leia novamente e verá que eu em momento algum disse pra você não usar).
Tentando novamente ajudar um pouco, responda as perguntas a si mesmo:
1- Você sabe usar reflection?
2- Você consegue refatorar um código valendo-se dos conceitos de orientação a objetos e padrões de projeto?
Se não puder responder “sim” para as duas perguntas acima, desconsidere usar o código citado ou estude bastante antes de colocá-lo em produção. Eu, como cliente, iria detestar usar um produto cujo próprio fabricante desconhece o que colocou lá dentro (sei que existem vários por aí e que eu, sim, provavelmente uso, mas digo isso porque eu me recuso a colocar coisas que não conheço em algo que estou fazendo).
Agora, se for ficar de mimimi é melhor mudar de fórum mesmo, pois apontar erros e sugerir outras soluções (como estamos fazendo aqui) é, sim, uma forma de ajudar e é, sim, como os participantes sérios do GUJ costumam fazer.
R
rodrigo.uchoa
Boa tarde, pessoal,
Vejo que estão criticando código que foi postado, como solução para meu problema, peço a vocês que apresentem um solução para tal. Vim aqui com o intuito de pedir ajuda, quando apresentei uma solução para compartilhar com todos, criticam aquilo que postei. Acho que me enganei com este fórum.
Obrigado.
Até entendo seu desespero, mas é claro que aqui dificilmente vão fazer tudo pra você. O máximo que as pessoas podem fazer é apontar caminhos. Já foi dado milhares de caminhos aqui que você pode seguir. Como já foi dito acima, escolha um dos caminhos e depois pergunte pontualmente sobre cada problema que poder vir a ter.
E
eude.lacerda
rodrigo.uchoa:
Boa tarde, pessoal,
Vejo que estão criticando código que foi postado, como solução para meu problema, peço a vocês que apresentem um solução para tal. Vim aqui com o intuito de pedir ajuda, quando apresentei uma solução para compartilhar com todos, criticam aquilo que postei. Acho que me enganei com este fórum.
Obrigado.
Até entendo seu desespero, mas é claro que aqui dificilmente vão fazer tudo pra você. O máximo que as pessoas podem fazer é apontar caminhos. Já foi dado milhares de caminhos aqui que você pode seguir. Como já foi dito acima, escolha um dos caminhos e depois pergunte pontualmente sobre cada problema que poder vir a ter.
Não quero que façam por mim. Estou irritado devido… puxa! Foi a única solução que encontrei, que atendeu a minha necessidade, cuja a que tentei não deu, Hibernate Envers. E que o projeto pede, então, não vejo que estou querendo que façam por mim.
R
rodrigo.uchoa
Relaxa. Se a única que deu certo foi aquela que você postou o código, vai com ela por enquanto e na medida do possível tenta melhorar os pontos que foram ditos aqui.
Só quem conhece o seu problema de verdade é você, pra saber a melhor solução.
J
javaflex
eude.lacerda:
Boa tarde, pessoal,
Vejo que estão criticando código que foi postado, como solução para meu problema, peço a vocês que apresentem um solução para tal.
Trigger.
H
Hebert_Coelho
eude.lacerda:
Boa tarde, pessoal,
Vejo que estão criticando código que foi postado, como solução para meu problema, peço a vocês que apresentem um solução para tal. Vim aqui com o intuito de pedir ajuda, quando apresentei uma solução para compartilhar com todos, criticam aquilo que postei. Acho que me enganei com este fórum.
Obrigado.
Entenda que não é só por que foi a “única”, não quer dizer que é viável. As vezes vale a pena rever os requisitos, ou até mesmo, as tecnologias.
Como foi dito, várias soluções foram apresentadas. Qualquer opção que você escolhesse estaria sujeita à críticas, umas mais que outras.
OBS.: Se você for reagir desse modo toda vez que alguém bater de frente com algo que você fez, a vida vai ser ainda mais difícil para você. O jeito é sempre parar e analisar: “o que tem de errado nessa situação? o que pode ser mudado?” [=
I
Impossivel
Se o líder aprovou então vá em frente…
L
leandronsp
Sensacional!
–
Previsão para o futuro:
“Daqui 4 meses o requisito muda, você vai olhar pra esse código, não vai entender bulhufas, vai buscar outra solução pra auditoria e de quebra, muito provavelmente, irá precisar migrar oq foi auditado durante os 4 meses. Deixar numa tabela só, hmmmmm, sei ñao.”
SGBD são sistemas que existem há trocentos anos, são mantidos de forma séria e resolvem diversos problemas comuns de infraestrutura. Como a galera, já destacou, “Trigger for the rescue!”.
E
eude.lacerda
Galera,
O requisito é esse: “Toda e qualquer INSERÇÃO / ALTERAÇÃO / EXCLUSÃO, deve ser registrada pelo sistema”. Foi exigência do cliente. Então, pronto.
I
Impossivel
Como disse anteriormente, se o líder aprovou, ignore os haters…
eude.lacerda:
Galera,
O requisito é esse: “Toda e qualquer INSERÇÃO / ALTERAÇÃO / EXCLUSÃO, deve ser registrada pelo sistema”. Foi exigência do cliente. Então, pronto.
Apenas duas colocações:
As pessoas estão criticando a solução (como foi feito) e não a especificação (o que é pra ser feito).
A especificação é feita pelo analista e a solução aprovada pelo líder do projeto, em ambos os casos o cliente não tem na a ver com a historia.
H
Hebert_Coelho
eude.lacerda:
Galera,
O requisito é esse: “Toda e qualquer INSERÇÃO / ALTERAÇÃO / EXCLUSÃO, deve ser registrada pelo sistema”. Foi exigência do cliente. Então, pronto.
Então de onde saiu o requisito de que coluna A deve ser salva e a B não?
B
bomba544
Como foi dito aqui, a melhor solução é tratar isso a nível de branco de dados.
Onde trabalho, temos uma tabela de log onde registra toda e qualquer INSERÇÃO, ATUALIZAÇÃO E DELEÇÃO de dados. Tudo é feito por trigger/procedures/functions. Como foi passado ali em cima, se alguém de FORA da aplicação acessar, fica fácil furar esse “log” e mexer nas tabelas sem que isso fosse gravado em algum lugar.