Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Teste Interessante: Tranação e Session Bean x Resource JAX-RS

Queria fazer um teste para ter uma outra visão a respeito do escopo transacional dos sessions beans, e para tal, fiz o teste descrito abaixo:

  1. Retirei o conteúdo do método "enviarEmail" do AgendamentoEmailJob e o coloquei no AgendamentoEmailResource, fazendo uma pequena alteração. Coloquei-o no "listar" mesmo, só pra fins de testes. Ficou dessa forma:
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    public Response listar() {
        List<AgendamentoEmail> listaEmailsNaoAgendados 
        = agendamentoEmailServico.listarPorNaoAgendado();

        listaEmailsNaoAgendados.forEach(emailNaoAgendado -> {
            //agendamentoEmailServico.enciar(emailNaoAgendado);
            agendamentoEmailServico.alterar(emailNaoAgendado);
        });

        return Response.ok(agendamentoEmailServico.listar()).build();
    }

Ao fazer a requisição para esse método, a rotina é executada e no banco de dados, o resultado é o mesmo quando se faz a anotação "@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)" (apenas alguns registros são alterados).

A conclusão na qual cheguei é: sempre que um Session Bean utiliza outro, o contexto transacional sempre fica com o session bean que "faz a chamada" (não vou chamar de pai pra não confundir com herança), e esse comportamento faz com que, caso haja uma sequência de alterações no banco de dados, se houver alguma exceção no session bean que "recebeu o chamado", todas as alterações sofrem rollback, como demonstrado no vídeo.

Porém, caso essa sequência de alterações seja solicitada por um outro componente que não seja um session bean (ex: um resource JAX-RS), o contexto transacional fica somente no método que recebeu a solicitação e faz parte de um session bean.

Gostaria apenas de saber se o raciocínio está correto, porque procurei na documentação, mas não encontrei.

Desde já, muito obrigado!

6 respostas

Everton, boa noite. Você precisa verificar também se o recurso não está sendo tratado como @Transactional. Esse é um código perfeitamente funcional:

@POST
@PermitAll
@Transactional
@Consumes(MediaType.APPLICATION_JSON)
public void inserir(User user) {
    userRepository.add(user);
}

Caso esteja, as regras de transação irão valer da mesma maneira. Um bom artigo que temos aqui na alura é este aqui: https://www.alura.com.br/artigos/jta-java-transaction-api

É explicado com mais detalhes estas questões acima. =)

Qualquer outra dúvida estamos à disposição.

Olá João! Que interessante!

Uma dúvida surgiu:

Você disse que é um código perfeitamente normal. Gostaria de saber o um pouco mais sobre, porque nas minhas aplicações, geralmente utilizo o @Transactional nos DAOs e, no máximo, nos services (especificamente nos métodos que envolvem a persistência de dados em mais de uma entidade, para que os dados sejam persistidos em todas as tabelas, ou em nenhuma). Entendo um resource como um componente que faz parte da camada Controller, e entendo uma anotação de transação como sendo algo ou de um DAO, ou de uma regra de negócio (como no exemplo já citado, ou persiste tudo, ou nada).

Baseado nisso, de fato é normal utilizar uma anotação de transação em um método de um resource?

Pensando melhor aqui, talvez eu não tenha me expressado corretamente (editei a resposta). Acho que a ideia era mostrar que é um código funcional e dessa maneira a gente teria um cenário diferente do que estava em discussão. Ficou melhor né? hahaha.

Mas enfim, o "normal" abre para discussão e assim a gente precisa estudar o cenário direitinho. Hoje nós temos no spring a interface JpaRepository, que nos fornece métodos de persistência. Eu posso injetar minha interface que estende de JpaRepository na minha (controller ou resource) e isso seria válido. Se eu preciso de um método transacional, onde estaria a anotação? A mesma situação vale quando estamos trabalhando com o Panache Entity usando active records. As nossas entidades serão as responsáveis por fornecer os métodos de persistência de dados e não precisamos (não é regra) ter um service para trabalhar com estes recursos, eu poderia chamar os métodos da entidade em um método da controller ou resource em um contexto transacional.

Talvez para uma aplicação EJB, o mais comum é vermos o cenário que você colocou acima, onde um JAX da vida chama o EJB e a partir dai temos um controle transacional, mas isso não impediria de haver um recurso anotado como @Transactional.

João Victor, que massa!

Acho que isso abre espaço para outra discussão: você já parou para pensar na transformação que a Engenharia de Software vem sofrendo nos últimos 10 anos?

Quando comecei a estudar EngSoft, em 2007/2008, essas questão da separação de responsabilidade por camadas era tipo um dogma. Quem faz isso está trabalhando com responsabilidade, e quem não faz está trabalhando com gambiarra.

Inventaram até um termo na época: POG (Programação Orientada a Gambiarra). kkkkkkkkkkkk...

Ahh, e java era tipo o Monte Olimpo dos desenvolvedores.

Atualmente, no entanto, a galera tem rejeitado bastante a organização que o java propõe, falando mal da verbosidade do java e enaltecendo a facilidade (talvez um sinônimo aqui possa ser agilidade) de frameworks mais recentes (como Node) propõem.

Vi inclusive aqui na Alura, no evento Imersão Carreira Tech, uma desenvolvedora de uma empresa renomada falando que não tinha problema, em algumas exceções, colocar um SQL no controller ou na view (não estou muito bem lembrado em qual dos dois ela falou).

Algumas questões interessantes para um ótimo debate seriam:

  1. Quais outras mudanças na Engenharia de Software podemos identificar nos últimos 10 anos?

  2. Qual a porcentagem provável que a cultura agile influenciou nessas mudanças?

  3. Será que existe um meio-termo entre uma boa arquitetura e uma boa velocidade de desenvolvimento? E o que sairia quando cruzamos esses dois últimos com a manutenibilidade futura?

  4. E, com base em tudo isso, o que afinal seria uma boa arquitetura?

(vou deixar um pouco de fora o conceito de "boa" pra não juntar com o conceito de qualidade, pq acho que isso já nos conduz para novas discussões... kkkkkk)

E aí, curtiu as provocações? :D

solução!

Muito legal essas questões. Falando sobre o desenvolvimento em camadas, eu particularmente gosto, porém deve fazer sentido. Se eu tenho no meu service apenas mais uma camada para chamar meus métodos do repositório, eu não acho que seja uma boa opção, dessa forma prefiro chamar direto do controller, mas se eu tenho regras negociais, eu particularmente gosto de coloca-las em um service. Mas é como estamos falando desde o inicio, vai do desenvolvedor, da empresa e outras variáveis que interfira nisso haha com a imensidão de frameworks surgindo quase que diariamente, cada qual com as suas particularidades, é difícil cravar algo como lei.

Verde, você tem razão.

Até porque, um outro ponto bem interessante, é que a existência de camadas em uma aplicação não implica na sua utilização correta.

Já vi bastante, no caso de desenvolvedores iniciantes, quebrarem uma mesma regra de negócio e espalharem seus pedaços por toda a aplicação: parte no controller, parte no service, parte no DAO e até mesmo uma outra parte no BD, em Stored Procedures.

Eu mesmo já cometi vários desses erros quando estava iniciando (o exemplo do stored procedure recapitulei de uma prática antiga minha... kkkkk).

Bom, mas é isso. Muito obrigado pelas reflexões, aprendi bastante! :D