Olá, estou desenvolvendo uma aplicação para estudos com VRaptor e segui esse guia no cookbook (http://vraptor.caelum.com.br/pt/cookbook/interceptando-recursos-anotados/), porém, msm sem a anotação @Transacional em meu método, o mesmo continua abrindo/fechando transações.
Eu deveria fazer mais alguma coisa para ele parar de realizar esse tipo de operação ? Ou seja, só abrir e fechar as transações quando o método tiver a anotação @Transacional ?
Obrigado.
Você trocou o método accepts para esse aqui??
É nesse método que contém a lógica para interceptar ou não.
public boolean accepts(ResourceMethod method) {
return method
.getMethod() //metodo anotado
.isAnnotationPresent(Transactional.class)
|| method
.getResource() //ou recurso anotado
.getType()
.isAnnotationPresent(Transactional.class);
}
Sim, o problema é nos métodos que não tem o @Transacional, pq mesmo ele retornando false no accepts, ele está abrindo e fechando a conexão no início e no final de todos os métodos, o que eu não quero.
Tanto, que se eu colocar @Transacional em algum método, ele me dá erro falando que a conexão já está ativa quando ele passa no intercept.
Então, o que eu não entendo, é que msm com o Interceptor ele continua abrindo e fechando as conexões em todos os métodos, e é isso que eu quero evitar.
Essas transações são iniciadas no próprio interceptor ou você tem algum outro lugar que as inicia?
Lembre-se que abrir transação é diferente de abrir sessão.
Sim, com certeza é diferente ! Mas pelo que entendi, antes de criar o Interceptor, o VRaptor trata essas transações para mim correto ? Sempre abrindo e fechando no início e final dos métodos.
Por exemplo:
public teste(Object o){
// Nesse caso msm sem a transação, ele faz transacao.begin();
dao.adiciona(o);
// E depois transacao.commit();
}
Como eu desabilito isso para ele fazer somente quando tiver a anotação @Transacional ? Quero que dê erro no dao.adiciona falando que não tem transação !! rsrs
Desse jeito mesmo. Me mostra o seu interceptor…
E o seu controller.
O fonte está em casa, mas o interceptor está rigorosamente igual ao do cookbook. E o controller é igual o adiciona que fiz acima, só que no caso é inserção de usuário igual a FJ21.
Só com o interceptor, ele já deveria parar de abrir e fechar conexões sozinho ? E respeitar minha anotação ? Ou teria que fazer algo mais para “desabilitar” isso ?
É… Só mudando o método accepts para esse novo formato que já funciona…
Hmmm, msm com o interceptor ele continua abrindo e fechando conexões em todos meus métodos, msm sem a anotação.
Bom, depois você me mostra essas duas classes…
Faça um debug e veja se ele está retornando true no método accepts, se estiver, é por que ele encontrou a anotação, ai você pode ter anotado algum lugar errado…
Quando eu coloco a anotação ele está retornando TRUE certinho. O meu problema é que mesmo os métodos que não tem anotação estão abrindo e fechando a transação, mesmo o accepts retornando FALSE !! Logo, não é meu interceptor que está abrindo e fechando ela, é outra coisa, pq nesse caso de retornar FALSE ele nem entra no intercepts, e é essa outra coisa que eu queria fazer parar, entendeu ?
Provavelmente o interceptor padrão do vraptor, que abre a transação sempre, ainda está ativo. Você precisa desativa-lo não usando o pacote br.com.caelum.vraptor.util.hiberante/jpa.
Outra solução é alterar teu componente para sobrescrever o padrão do vraptor:
[quote=garcia-jj]Provavelmente o interceptor padrão do vraptor, que abre a transação sempre, ainda está ativo. Você precisa desativa-lo não usando o pacote br.com.caelum.vraptor.util.hiberante/jpa.
Outra solução é alterar teu componente para sobrescrever o padrão do vraptor:
Obrigado, acredito que seja isso mesmo… vou testar a noite e retorno !! Se não me engano, coloco algo sobre esse pacote no web.xml em alguma config mesmo…
É, não funcionou, rsrs… vou postar minhas classes… (Só lembrando que uso JPA)
Interceptor
[code]@Intercepts
public class TransactionInterceptor implements Interceptor {
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Transactional {
}
private EntityTransaction utx = null;
public TransactionInterceptor(EntityManager em) {
this.utx = em.getTransaction();
}
public void intercept(InterceptorStack stack, ResourceMethod method,
Object instance) {
try {
utx.begin();
stack.next(method, instance);
utx.commit();
} finally {
if (utx.isActive()) {
utx.rollback();
}
}
}
public boolean accepts(ResourceMethod method) {
return method.getMethod().isAnnotationPresent(Transactional.class)
|| method.getResource().getType()
.isAnnotationPresent(Transactional.class);
}
}[/code]
Método do Controller
@Post("/usuarios")
public void adiciona(Usuario usuario) {
if (dao.existeUsuario(usuario)) {
validator.add(new ValidationMessage("Login já existe",
"usuario.login"));
}
validator.onErrorUsePageOf(UsuariosController.class).novo();
dao.adicionar(usuario);
result.redirectTo(ProdutosController.class).lista();
}
Web.xml (Caso sirva de algo)
[code]
br.com.caelum.vraptor.packages
br.com.caelum.vraptor.util.jpa
<!-- configura o controlador do VRaptor -->
<filter>
<filter-name>vraptor</filter-name>
<filter-class>br.com.caelum.vraptor.VRaptor</filter-class>
</filter>[/code]
Nesse caso, o método adiciona só deveria funcionar se tivesse a anotação @Transactional, porem, msm sem ela, algo está abrindo e fechando a conexão, e é isso que quero evitar…
vc tem que tirar esse pacote:
<context-param>
<param-name>br.com.caelum.vraptor.packages</param-name>
<param-value>br.com.caelum.vraptor.util.jpa</param-value>
</context-param>
ele é quem está abrindo e fechando a transação. Só que vc teria que fazer os componentFactory’s de EntityManager e EntityManagerFactory…
alternativa a isso é manter o pacote da jpa registrado e criar essa classe:
@Component
public class MeuTransactionInterceptor extends JPATransactionInterceptor {
//sobrescreve o método accepts com a implementação que vc postou
}
daí só precisa do accepts, o intercept já tá implementado.
Hmmm eu já tinha tentado isso, mas não rolou… fiz certo né!?
[code]@Intercepts
@Component // nao sei se precisa disso msm, mas como vc colocou no exemplo eu segui
public class TransactionInterceptor extends JPATransactionInterceptor implements
Interceptor {
public TransactionInterceptor(EntityManager manager, Validator validator) {
super(manager, validator);
this.utx = manager.getTransaction();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Transactional {
}
private EntityTransaction utx = null;
public void intercept(InterceptorStack stack, ResourceMethod method,
Object instance) {
try {
System.out.println("intercept");
utx.begin();
stack.next(method, instance);
utx.commit();
} finally {
if (utx.isActive()) {
utx.rollback();
}
}
}
public boolean accepts(ResourceMethod method) {
System.out.println("accepts");
return method.getMethod().isAnnotationPresent(Transactional.class)
|| method.getResource().getType()
.isAnnotationPresent(Transactional.class);
}
}[/code]
Ele continua controlando a transação…
Eu também tentei apenas criar a classe como vc falou:
[code]@Component
public class MeuTransactionInterceptor extends JPATransactionInterceptor {
public MeuTransactionInterceptor(EntityManager manager, Validator validator) {
super(manager, validator);
System.out.println("MeuTransactionInterceptor");
}
}[/code]
Mas ainda assim, o JPA continua controlando a transação… acho que não entendi algo do que vc disse… poderia explicar melhor ?
deixa a classe do jeito que eu falei e implementa o accepts!
retorna false no accepts, por exemplo, daí nada vai ficar com transação.
Meu web.xml eu não alterei nada… minhas classes ficaram:
[code]@Intercepts
public class TransactionInterceptor implements Interceptor {
public TransactionInterceptor(EntityManager manager) {
this.utx = manager.getTransaction();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Transactional {
}
private EntityTransaction utx = null;
public void intercept(InterceptorStack stack, ResourceMethod method,
Object instance) {
try {
utx.begin();
stack.next(method, instance);
utx.commit();
} finally {
if (utx.isActive()) {
utx.rollback();
}
}
}
public boolean accepts(ResourceMethod method) {
return method.getMethod().isAnnotationPresent(Transactional.class)
|| method.getResource().getType()
.isAnnotationPresent(Transactional.class);
}
}[/code]
[code]@Component
public class MeuTransactionInterceptor extends JPATransactionInterceptor {
public MeuTransactionInterceptor(EntityManager manager, Validator validator) {
super(manager, validator);
}
public boolean accepts(ResourceMethod method) {
return false;
}
}[/code]
Certo? Mas não ta rolando… ele nem passa pelo accepts…
então tira o pacote da jpa do web.xml… e recria as componentFactories… o interceptor deveria funcionar… ele não passa nem pelo construtor?