sexta-feira, 27 de outubro de 2017

Cuidado com Try/Catch no EJB e Spring!

Ao criarmos um método geralmente colocamos blocos de código dentro de Try/Catch para podermos tratar os possíveis erros e o sistema continuar seu trabalho. Este é talvez o principal problema de integridade e consistência de dados quando as transações são usadas. Veja o exemplo a seguir:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public TradeData placeTrade(TradeData trade) throws Exception {
    try {
        insertTrade(trade);
        updateAcct(trade);
        return trade;
    } catch (Exception up) {
       //log the error
       throw up;
    }
}

Suponha que a conta não tenha fundos suficientes para comprar o estoque em questão ou não está configurado para comprar ou vender estoque ainda e lança uma exceção verificada (por exemplo, FundsNotAvailableException). A ordem comercial – insertTrade(trade) - persistiu no banco de dados ou a unidade lógica inteira do trabalho foi revertida? A resposta, surpreendentemente, é que, após uma exceção verificada (no Spring Framework ou EJB), a transação compromete qualquer trabalho que ainda não tenha sido comitado. Isso significa que se uma exceção verificada ocorrer durante o método updateAcct() , a ordem comercial foi persistida, mas a conta não foi atualizada para refletir a transação financeira.

As exceções de tempo de execução (ou seja, exceções não verificadas) forçam automaticamente a transação EJB/Spring a reverter completamente, mas as exceções verificadas não. Isso não é de todo ruim ou errado, pois muitas vezes precisamos tratar os erros e deixar o sistema prosseguir. Por exemplo, se em um processo, precisamos enviar um e-mail com as operações salvas no banco, mas o servidor de e-mail estiver fora, qual o mais importante? Salvar os dados ou enviar o e-mail? Então tratamos o envio do e-mail e deixamos os dados serem salvos.

Mas e quando for o caso em que precisarmos abortar tudo?

A anotação @TransactionAttribute encontrada na especificação EJB não inclui diretivas para especificar o comportamento de reversão. Em vez disso, você deve usar o método SessionContext.setRollbackOnly() para marcar a transação para rollback em um local específico, ou, o mais aconselhavel é criar uma exceção que force esse rollback com a anotação @ApplicationException setando  a propriedade rollback = true. Vamos ver primeiro ver como fazer a exceção funcionar apenas para um método:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public TradeData placeTrade(TradeData trade) throws Exception {
    try {
       insertTrade(trade);
       updateAcct(trade);
       return trade;
    } catch (Exception up) {
        //log the error
        sessionCtx.setRollbackOnly();
        throw up;
    }
}

Uma vez que o método setRollbackOnly() é invocado, você não pode mudar de idéia; O único resultado possível é reverter a transação após a conclusão do método que iniciou a transação.

           Para pegar a sessão em um EJB faz-se daseguinte maneira:

@Resource
SessionContext context;
 

           A segunda opção que é criar uma Exceção e anotá-la segue abaixo:

@ApplicationException(rollback=true) 
public class RegraNegocioException extends Exception
{
   ...
}
 
Já no Spring Framework você especifica esse comportamento do rollback através do parâmetro rollbackFor na anotação @Transactional, conforme mostrado a seguir:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public TradeData placeTrade(TradeData trade) throws Exception {
    try {
           insertTrade(trade);
updateAcct(trade);
           return trade;
    } catch (Exception up) {
           //log the error
           throw up;
    }
}

Esses são problemas difíceis de serem detectados no momento do desenvolvimento e de testes, pois geralmente ocorrem quando o processamento da aplicação e o acesso ao banco de dados estão muito altos.

Mas atenção! Essa solução funciona se você tomar cuidado com as armadilhas do REQUIRED_NEW, como mostro nesse outro post:

http://olamundo-java.blogspot.com.br/2017/10/armadilhas-do-requirednew-transaction.html

        Espero que esse post ajude. A fonte pela qual me baseei para escrever esse post segue abaixo:



quarta-feira, 25 de outubro de 2017

Armadilhas do REQUIRED_NEW transaction no EJB e Spring

Pesquisando sobre um erro de duplicação de dados e problemas com transações no EJB encontrei essa explicação muito interessante. O atributo REQUIRED_NEW tanto no EJB quanto no Spring podem ter resultados inesperados e levar a dados corrompidos e inconsistentes. Isso pode ocorrer devido o fato de ele sempre iniciar uma nova transação quando o método é chamado, mesmo já existindo uma transação ou não.

Veja o Exemplo:

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception
{
          ...
}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateAcct(TradeData trade) throws Exception
{
   ...
}

Nesse caso os 2 métodos são públicos e estão anotados com REQUIRED_NEW. Quando chamados de forma separada, não há problema. Porém, se um for usado os dois métodos dentro de uma mesma unidade de trabalho transacionada, como por exemplo um outro método, isso pode causar inconsistências. Por exemplo, digamos que dentro do método insertTrade() você chame o método updadeAcct(), se ocorrer um rolled back depois do chamada do updateAcct(), ele já vai ter efetuado o commit no banco de dados, sendo desfeito apenas o que pertence ao insertTrade().

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {
       em.persist(trade);
       updateAcct(trade);
      //exception occurs here! Trade rolled back mas updateAcct(trade) não!
      ...
}



Isso acontece porque uma nova transação é iniciada no método updateAcct() criando uma árvore de transações, e houve um commit quando ele terminou sua execução, independentemente do que ocorra em outra transação da árvore. Devido a esse comportamento, o atributo de transação REQUIRES_NEW deve ser usado somente se a ação do banco de dados no método que está sendo invocado precisa ser salva no banco de dados independentemente do resultado da transação do topo da árvore (como uma funcionalidade de auditoria por exemplo).

O ponto principal é sempre usar o atributo MANDATORY ou REQUIRED vez de REQUIRES_NEW menos que você tenha um motivo para usá-lo.

         Esses são problemas difíceis de serem detectados no momento do desenvolvimento e de testes, pois geralmente ocorrem quando o processamento da aplicação e o acesso ao banco de dados estão muito altos.

          Aconselho uma passada por esse outro post que deixo o link logo abaixo também, pois traz um outro problema que pode acarretar em erros semelhantes de inconsistência de dados na aplicação.


terça-feira, 17 de outubro de 2017

Removendo um elemento de uma Collection durante um laço "for" no Java

Erros simples as vezes nos fazem perder tempo. As vezes temos uma coleção de objetos e em determinado momento precisamos percorrer essa coleção e remover alguns objetos de dentro dela, porém somos surpreendidos com uma mensagem de erro. O foreach não permite que façamos isso, pois ele acaba se perdendo na iteração.

Para resolver o problema, use o Iterator do java.

private void preencheCarroComCabecalho(
            final List<BtpCarro> lstBtpCarros, BtpCabecalho btpCabecalho)
{
        for (final Iterator<BtpCarros> iterator = lstBtpCarros
                .iterator(); iterator.hasNext();)
        {
            final BtpCarro btpCarro = iterator
                    .next();
            if ( btpCarros.getCarSeqCarro() != null )
            {
                btpCarros.setMarca(cabecalho.getMarca());
                btpCarros.setAno(cabecalho.getAno());
                btpCarros.setCor(cabecalho.getCor());
            }
            else
            {
      iterator.remove();
            }
        }
}

terça-feira, 10 de outubro de 2017

JPA - Quando e como usar uma Entidade ou um VO(DTO) na minha aplicação?


Bem, para começarmos vamos definir algumas nomenclaturas usadas em padrões de projeto de desenvolvimento.

Java Bean – Uma classe Java simples, com propriedades privadas, métodos de acesso, construtor padrão. Daí saem ideias como Bean CDI, ManagedBean JSF, etc. Também são chamados de POJO (Plain Old Java Object), que quer dizer nada mais nada menos que “bom e” Velho Objeto Java Padrão.

PO ou Entidade – Um bean Java que pode ser persistido no banco de dados. Também conhecido como PO (Persistent Object). Nomenclaturas muito usadas quando se trabalha com frameworks de persistência ORM como o Hibernate.  Trata-se de uma classe Java com propriedades privadas, métodos de acesso, construtor padrão, e métodos de identidade como equals e hashCode. Também devem implementar a interface Serializable. Podem ter sobrecarga de construtores e alguns métodos específicos da classe em alguns casos. Suas propriedades são persistidas no banco de dados.

VO (Value Object) – São objetos Java usados para guardar valores em memória, geralmente para usar na camada de visão, não sendo persistidos no banco de dados. Quando se há a necessidade de persistir parte de suas propriedades ou todas elas, elas são passadas para uma instancia de uma Entidade. São muito semelhantes (se não iguais) ao uso dos DTO’s (Data Transfer Object). Eu particularmente considero exatamente a mesma coisa.

      Dada a devida explicação, vemos que basicamente as Entidades tem suas propriedades persistidas no banco enquanto os VO’s são usados para exibir dados na camada de visão ou coisas semelhantes, desde que não envolva persistência.

A primeira pergunta é: Por quê?

Se eu posso usar o objeto que é a própria entidade para exibir dados na camada de visão ou fazer qualquer outra coisa, por que usar um VO ou DTO?

Basicamente por 2 motivos:

1.        A Entidade é engessada. Uma Entidade se comporta como uma estrutura engessada na hora das consultas, dificultando queries nativas, e impedindo de mesclar propriedades de Entidades diferentes. Te obriga a montar as Entidades que compõe a Entidade principal, o que pode trazer um grande encadeamento dependendo da complexidade das consultas.

2.        Melhora de performance. Entidade pode ser muito grande e pesada, e você pode estar precisando apenas de 5 ou 10 propriedades, que podem inclusive serem montadas em Tipos Nativos do Java, diminuindo o grau de encadeamento destas, o que melhora a performance.

A segunda é: Quando usar VO e Quando usar Entidade?

Use Entidades quando os dados forem ser persistidos ou quando de fato você for carregar todas ou a maioria das propriedades, ou a entidade for simples.

Use VOs quando for criar Relatórios, disponibilizar os dados em um WebService, quando quiser juntar dados de diferentes Entidades, etc.; e quando precisar fazer consultas nativas complexas.

A terceira e mais importante é: como usar?

Existem algumas formas de preencher o VO a partir de uma consulta, e isso muda dependendo da forma como a consulta é feita ou o VO é construído. Por exemplo, se a consulta é nativa ou não, se o VO está ou não anotado com anotações do JPA, se um construtor é passado dentro da consulta, etc. Na tabela abaixo vou listar as 3 formas, depois irei dar um exemplo de cada uma delas:

VO
Como preencher na consulta SQL
VO com anotações JPA
Consulta nativa passando a classe para o EntityManager. Ex: “entityManager.createQuery(consulta, classeVO.class)”
VO sem anotações JPA
Consulta JPQL usando o construtor do VO na consulta, sem passar a classe para o entityManager.
VO sem anotações JPA
Consulta nativa sem passar a classe do VO para o entityManager e sem usar construtor do VO na consulta, porém preenchendo o VO percorrendo o resultList que será uma lista de Object onde cada Object desse é um Array de Object (Object[]) e em cada posição do array se encontra uma propriedade retornada na consulta.


Vamos analisar primeiro um VO com anotações do JPA e a consulta nativa. Segue o VO:

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class VeiculoVO implements Serializable
{
     private static final long serialVersionUID = -5022603734460617635L;

     @Id
     @Column(name = "SEQ_VEICULO")
     private Long seqVeiculo;

     @Column(name = "SEQ_LICENCA")
     private Long seqLicenca;

     @Column(name = "VEI_DSC_PLACA")
     private String placa;

     @Column(name = "COR")
     private String cor;

     @Column(name = "MODELO")
     private String modelo;

     public VeiculoVO()
     {

     }

     public VeiculoVO(final Long seqVeiculo, final Long seqLicenca, final String placa, final String cor, final String modelo)
     {
           super();
           this.seqVeiculo = seqVeiculo;
           this.seqLicenca = seqLicenca;
           this.placa = placa;
           this.cor = cor;
           this.modelo = modelo;
     }
    
     // demais getters and setters, equals e hashCode
}

Note que por ser um VO ele pode conter campos de diferentes Entidades, uma vez que seu papel é justamente ser um auxiliar. Apesar de anotado com as anotações do JPA, não informei nenhuma tabela justamente porque ele não tem esse vínculo no banco de dados.

Os nomes dado aos campos na anotação @Column devem ser os mesmos retornados na consulta!

Vamos ao exemplo de uso: uma consulta nativa onde a classe é passada para o EntityManager junto com o SQL no momento da criação da query:

final StringBuilder sb = new StringBuilder();

sb.append(" SELECT "
+ "     (SELECT LIV.SEQ_LICENCA_AMBIENTAL FROM EMT_EMPRESA_TRANSBORDO EMT "
+ "           INNER JOIN LIA_LICENCA_VEICULO LIV ON LIV.EMT_SEQ_EMPRESA = EMT.EMT_SEQ_EMPRESA "
+ "           INNER JOIN VEI_VEICULO VEI2 ON VEI2.LIV_SEQ_LICENCA_VEICULO = LIV.LIA_SEQ_LICENCA_VEICULO "
+ "           WHERE VEI2.VEI_DSC_PLACA = VEI.VEI_DSC_PLACA) AS SEQ_LICENCA, "
+ "     VEI.VEI_SEQ_VEICULO AS SEQ_VEICULO, VEI.VEI_DSC_PLACA AS VEI_DSC_PLACA, "
+ "     VEI.VEI_DSC_COR AS COR, VEI.VEI_DSC_MODELO AS MODELO "
+ " FROM EMT_EMPRESA_TRANSBORDO EMT "
+ " INNER JOIN LIV_LICENCA_VEICULO LIV ON LIA.EMT_SEQ_EMPRESA = EMT.EMT_SEQ_EMPRESA "
+ " INNER JOIN VEI_VEICULO VEI ON VEI.SEQ_LICENCA_VEICULO = LIV.SEQ_LICENCA_VEICULO "
+ " INNER JOIN CON_CONTRATO CON ON EMT.CON_SEQ_CONTRATO = CON.CON_SEQ_CONTRATO "
+ " INNER JOIN CLI_CLIENTE CLI ON CON.CLI_SEQ_CLIENTE = CLI.CLI_SEQ_CLIENTE ");

final Query query = entityManager.createNativeQuery(sb.toString(),
                     VeiculoVO.class);
    
return query.getResultList();
    
Na Entidade Veiculo, a parte que está grifada de vermelho na verdade é uma Entidade chamada de Licenca, que guarda dados referentes a licença do veículo, mas no VO VeiculoVO só era necessário carregar o ID dessa entidade.

Agora vamos para um segundo caso, que não tem as anotações do JPA no VO, mas usamos JPQL:

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

public class VeiculoVO implements Serializable
{
     private static final long serialVersionUID = -5022603734460617635L;

     private Long seqVeiculo;

     private Long licSeqLicenca;

     private String placa;

     private String cor;

     private String modelo;

     public VeiculoVO()
     {

     }

public VeiculoVO(final Long seqVeiculo, final String placa, final Long seqLicenca)
{
super();
this.seqVeiculo = seqVeiculo;
this.placa = placa;
this.seqLicenca = seqLicenca;
}

     public VeiculoVO(final Long seqVeiculo, final Long seqLicenca, final String placa, final String cor, final String modelo)
     {
           super();
           this.seqVeiculo = seqVeiculo;
           this.seqLicenca = seqLicenca;
           this.placa = placa;
           this.cor = cor;
           this.modelo = modelo;
     }
    
     // demais getters and setters, equals e hashCode
}

Note que agora o VO não tem anotações JPA e nós queremos trazer apenas 3 campos dele dessa vez: a placa e o sequencial do veículo e o sequencial da licença. Para isso adicionamos o construtor adequado.

Vamos a consulta JPQL:

final StringBuilder sb = new StringBuilder();

sb.append(" SELECT NEW br.com.meuprojeto.modelo.VeiculoVO(
vei.seqVeiculo, vei.placaVeiculo, liv.licencaVeiculo) FROM Veiculo vei INNER JOIN vei.licencaVeiculo liv
");

final Query query = entityManager.createQuery(sb.toString());
    
return query.getResultList();

Agora vamos para o terceiro caso, onde não temos as anotações do JPA no VO, e usamos uma consulta nativa, observe que assumimos que o VO possui os getters and setters necessários:

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

public class VeiculoVO implements Serializable
{
     private static final long serialVersionUID = -5022603734460617635L;

     private Long seqVeiculo;

     private Long licSeqLicenca;

     private String placa;

     private String cor;

     private String modelo;

     public VeiculoVO()
     {

     }

     // construtores
    
     // demais getters and setters, equals e hashCode
}

Vamos a consulta SQL:

final StringBuilder sb = new StringBuilder();

sb.append(" SELECT VEI.VEI_SEQ_VEICULO, "
+ " VEI.VEI_DSC_PLACA FROM EMT_EMPRESA_TRANSBORDO EMT "
+ " INNER JOIN LIV_LICENCA_VEICULO LIA ON LIV.EMT_SEQ_EMPRESA = EMT.EMT_SEQ_EMPRESA "
+ " INNER JOIN VEI_VEICULO VEI ON VEI.SEQ_LICENCA_VEICULO = LIV.SEQ_LICENCA_VEICULO ");

final Query query = entityManager.createNativeQuery(sb.toString());
    
final List<?> resultList = query.getResultList();

Após a consulta SQL, teremos o resultList que é uma lista de Object onde cada Object desse é um Array de Object (Object[]) e em cada posição do array se encontra uma propriedade retornada na consulta. temos que trabalhar esses dados percorrendo a lista, recuperando os dados e colocando no VO:

final List<VeiculoVO> lstVeiculoVO = new ArrayList<>();

if ( resultList != null && resultList.size() > 0 ) {

for ( final Object value : resultList ) {

final VeiculoVO auxiliarVO = new VeiculoVO();
final Object[] result = (Object[]) value;

auxiliarVO.setSeqVeiculo( ( (BigDecimal) result[0] ).longValue() );

auxiliarVO.setPlaca( ( (String) result[1] ).toString() );
               
                lstVeiculoVO.add( auxiliarVO );
            }
        }

return lstVeiculoVO;


Pronto, essas são as formas de se trabalhar com VO no JPA. Espero que faça bom proveito!