[Resolvido] VRaptor3 - Problemas com EntityManager

Bom dia galera.

Estou com uma dúvida na parte de injeção de dependências do VRaptor3 que surgiu devido a um problema que estou enfrentando.

Iniciei um projeto e estou utilizando o JPA para persistência, criei um EntityManagerProvider para poder fazer uso do EntityManagerCreator e o EntityManagerFactoryCreator fornecidos pelo próprio VRaptor.

Criei meu DaoFactory que recebe o EntityManager no construtor, que é injetado normalmente pelo VRaptor.

public class DaoFactory {
    private EntityManager em;
    public DaoFactory(EntityManager em) {
        this.em = em;
    }
    //métodos omitidos
}

A partir do DaoFactory eu crios os daos

public ColaboradorDao getColaboradorDao() {
    return new ColaboradorDao(em);
}

O meu problema é o seguinte, eu criei uma implementação minha do DaoFactoryInterceptor que não abre a transação, optei por abri-la pela factory apenas nos métodos que realmente precisam de uma trasação, assim não abro uma transação apenas em métodos que só fazem consulta.
Acontece que ao fazer um teste se o meu rollBack estava funcionando, criei um método que simularia uma inclusão no meu ColaboradorController:

public void adicionar(Colaborador colaborador) {
    factory.beginTransaction();
    factory.getColaboradorDao().adicionar(colaborador);
}

Deixei sem o commit propositadamente para simular uma possível exception onde o rollBack deveria ser executado.
Mas ao retornar para o interceptor, a factory ve que existe uma transação aberta, executa o rollBack mas mesmo assim o objeto é persistido no banco.

Será que como eu recebo o EntityManager no construtor do ColaboradorDao ao instancia-lo está sendo injetado um EntityManager diferente do que foi injetado no DaoFactory ao invéz de usar o que eu passei como parâmetro?
Acredito que não seja isso porque o EntityManager é RequestEscoped correto?
Alguma luz do que pode estar acontecendo?

Obrigado
[]'s

Acho difícil que ele esteja retornando EntityManagers diferentes…

o que pode estar acontecendo é mais de uma transação diferente sendo aberta…

como você está abrindo a transação, e como você está fechando?

você não registrou o JPATransactionInterceptor, certo?

[]'s

Isso, não registrei o JPATransactionInterceptor.

Estou utilizando os seguintes métodos no DaoFactory para abrir e fechar a transação:

@Component
@RequestScoped
public class DaoFactory {
    private EntityManager em;
    private EntityTransaction transaction;

    public DaoFactory(EntityManager em) {
        this.em = em;
    }

    public void beginTransaction() {
        transaction = em.getTransaction;
        transaction.begin();
    }

    public void rollBack() {
        transaction.rollback();
        transaction = null;
    }

    public void close() {
        em.close();
    }

    public boolean hasTransaction() {
        return transaction != null && transaction.isActive();
    }

    //outros métodos
}

E no meu interceptor o código está assim:

public void intercept(Parametros......) {

    stack.next(method, instance);

    if (factory.hasTransaction()) {
        factory.rollBack();
        System.out.println("Tinha transação");
    }
    factory.close();

}

Eu recebo o DaoFactory no construtor tanto do Interceptor como no ColaboradorController, mas já fiz os testes aqui e a instancia é a mesma.

Inclusive a instancia de Transaction eh a mesma?

Se você está usando um appserver, não seria interessante deixar isso a cargo do container? Ou então se você está usando Spring deixa-lo tomar conta disso, ao invés de ficar controlando manualmente.

Sim, como o único lugar que pega a instância do EntityTransaction é no beginTransaction do DaoFactory é a mesma instância sim.

Estou usando Tomcat.
Essa arquitetura eu ainda vou alterar, só fiz desse modo para testar os métodos do DaoFactory se estão se comportando da maneira que devem.

Oi Guedes!

Deixa eu chutar: voce estaria usando linux+mysql?

Se for, o default do linux é o mysql criar base de dados MyISAM, que sao supe velozes mas nao possuem suporte a transacao. Voce precisa criar o banco com tabellas inno (InnoDB). Pra isso, mude o dialect para MySQLInnoDBDialect (ou algo assim).

Se nao for assim, é algo de auto commit que está ocorrendo por aí! (E voce tem certeza que o rollback esta sendo chamado no itnerceptador? Ele loga isso?)

abracos!

Opa, funcionou!!!

Chutou exatamente no ponto, realmente estou em ambiente Linux + MySql.
Alterei meu persistence.xml para o dialect MySQLInnoDBDialect, deletei e recriei o banco e funcionou na hora.

Falando com meu chefe antes de ir embora do escritório ele comentou comigo do autoCommit, variável que eu nem sabia se existia.

Mas engraçado que fiz um teste com o mesmo código no Vista, exportei o projeto do Eclipse no Linux e importei no Vista, e me foi apresentado o mesmo problema.
Bom… o importante é que funcionou.

Valeu.
:wink:

Mas agora surge uma dúvida.

Esse projeto que estou desenvolvendo, quando estiver em produção vai rodar em um Oracle.
Iniciei aqui com o MySql porque é o que eu já tenho aqui instalado e a princípio seria só eu trocar o dialect no persistence que o mesmo código passa a funcionar em um banco diferente.

Vocês recomendariam que eu já desenvolva direto no Oracle para evitar possíveis problemas na hora de colocar no ar?

Ola Guedes!

Ja fizemos mais de um projeto onde o ambiente de testes era o MySQL e o producao era o Oracle. Com o hibernate fica facil fazer essa migracao, e nos dois casos nao tivemos NENHUM problema. Mas sempre tinhamos um outro ambiente de testes em que faziamos os testes de integracao de tempo em tempo, pra garantir que nao haveria um problema maior.

abracos

[quote=guedes]Esse projeto que estou desenvolvendo, quando estiver em produção vai rodar em um Oracle.
Iniciei aqui com o MySql porque é o que eu já tenho aqui instalado e a princípio seria só eu trocar o dialect no persistence que o mesmo código passa a funcionar em um banco diferente.

Vocês recomendariam que eu já desenvolva direto no Oracle para evitar possíveis problemas na hora de colocar no ar?[/quote]

Tudo depende. Se você usar o banco de dados para armazenamento de dados apenas tudo funciona bem. Digo isso porque se você implementar muitas procedures e features proprietárias do banco você perde a portabilidade.

Eu tenho um projeto muito grande, aproximadamente 550 tabelas. No desenvolvimento usei pgsql, porém a versão final será em Oracle. Até o momento, onde já fiz 50% do software, tudo está funcionando bem. Tenho apenas as tabelas e algumas views. Nesse caso o mesmo script que uso no pgsql posso usar no Oracle. As tabelas eu uso a criação do próprio JPA.

Quando o projeto entrar em produção vou ter que fazer a importação dos dados do pgsql, porém já estou com um script planejado para fazer. A cada 10 dias faço um deploy no ambiente de QA com base oracle para testar se tudo funciona bem. Por enquanto tenho tudo sucesso nisso.

qual banco de dados você está usando? Alguns deles não suportam transação, daí as operações são
persistidadas antes…

Beleza pessoal, então vou continuar sem medo a produzir com o MySql, na hora de colocar em produção se der algum problema eu me viro.
xD

garcia-jj, será sim um banco apenas para armazenamendo e consulta de dados, sem views, procedures é um projetinho bem simples o que estou desenvolvendo.

lucascs, com a ajuda do Paulo já consegui resolver o problema.

Mais uma vez muito obrigado a todo mundo que ajudou.
:wink:

Abraços