Vraptor no GAE/J: Problemas ao usar JPA

33 respostas
G

Estive hoje fazendo uns testes rodando o Vraptor no GAE/J. Usei o projeto blank e tudo rodou bem. Porém quando coloquei o suporte a JPA estou tendo o problema abaixo. Pelo que notei o EntityManager não consegue ser inicializado. Há alguma configuração especial para usar o JPAProvider no GAE/J?

Caused by: javax.persistence.PersistenceException: Provider error. Provider: org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider at javax.persistence.Persistence.createFactory(Persistence.java:176) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:112) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:66) at br.com.caelum.vraptor.util.jpa.EntityManagerFactoryCreator.create(EntityManagerFactoryCreator.java:39) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597)

WARNING: Nested in org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'JPATransactionInterceptor': Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManager]: : Error creating bean with name 'br.com.caelum.vraptor.util.jpa.EntityManagerCreator': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerCreator': Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManagerFactory]: : Error creating bean with name 'br.com.caelum.vraptor.util.jpa.EntityManagerFactoryCreator': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactoryCreator': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Provider error. Provider: org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'br.com.caelum.vraptor.util.jpa.EntityManagerFactoryCreator': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactoryCreator': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Provider error. Provider: org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'br.com.caelum.vraptor.util.jpa.EntityManagerCreator': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerCreator': Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManagerFactory]: : Error creating bean with name 'br.com.caelum.vraptor.util.jpa.EntityManagerFactoryCreator': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactoryCreator': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Provider error. Provider: org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'br.com.caelum.vraptor.util.jpa.EntityManagerFactoryCreator': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactoryCreator': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Provider error. Provider: org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider: java.lang.NullPointerException at org.datanucleus.jpa.EntityManagerFactoryImpl.initialisePMF(EntityManagerFactoryImpl.java:452) at org.datanucleus.jpa.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:355) at org.datanucleus.store.appengine.jpa.DatastoreEntityManagerFactory.<init>(DatastoreEntityManagerFactory.java:63) at org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider.createEntityManagerFactory(DatastorePersistenceProvider.java:35) at javax.persistence.Persistence.createFactory(Persistence.java:172) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:112) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:66) at br.com.caelum.vraptor.util.jpa.EntityManagerFactoryCreator.create(EntityManagerFactoryCreator.java:39) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

33 Respostas

Luca

Olá

Viu o tópico que o Sérgio Lopes abriu a poucos dias atrás? É o mesmo problema do JPA brigar com o BigTable?

[]s
Luca

G

Luca, se é o tópico que estou pensando é sobre second level cache: http://www.guj.com.br/posts/list/205792.java.

Resolvi temporariamente o meu usando uma própria factory para JPA. Estou tentando outras formas sem precisar fazer isso.

Abraços

Luca

Olá

Isso mesmo. Me confundi aqui.

[]s
Luca

Lucas_Cavalcanti

o JPAProvider padrão do VRaptor procura um persistence-unit chamado default… no GAE ele TEM que se chamar transactions-optional…

daí tem que criar um EntityManagerFactoryCreator que usa esse outro nome

L
Como esse problema foi resolvido? Estou seguindo o sugerido pelo Lucas e mantendo o padrão para o GAE.
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
        </properties>
    </persistence-unit>

</persistence>
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class EMF {
    private static final EntityManagerFactory emfInstance =
        Persistence.createEntityManagerFactory("transactions-optional");

    private EMF() {}

    public static EntityManagerFactory get() {
        return emfInstance;
    }
}
Mas o erro continua, alguma sugestão?
Caused by: javax.persistence.PersistenceException: Provider error. Provider: org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider
	at javax.persistence.Persistence.createFactory(Persistence.java:176)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:112)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:66)
	at br.com.caelum.vraptor.blank.dao.EMF.<clinit>(EMF.java:8)
Lucas_Cavalcanti

essa classe está no classpath?
org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider

mais pra baixo na stacktrace tem alguma outra exception?

G

Ahh sim, falha minha em não colocar a resolução.

Criei uma classe que cria a factory conforme o indicado na documentação do GAE/J.

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class EMF {

	private static final EntityManagerFactory emfInstance = Persistence.createEntityManagerFactory("transactions-optional");

	private EMF() {
	}

	public static EntityManagerFactory get() {
		return emfInstance;
	}
}

E para usar:

public class EchoServlet extends HttpServlet {

	private static final EntityManager em = EMF.get().createEntityManager();

	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
				...
	}
}

E meu persistence.xml que fica em /src/META-INF:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
        </properties>
    </persistence-unit>

</persistence>

Acho que até dava para sobrescrever o provider de JPA padrão do Vraptor para usar a injeção de dependencias, mas como o projeto era apenas um teste não quis perder muito tempo.

Lucas_Cavalcanti

vc pode usar injeção de dependências tranquilamente… só criar ComponentFactories para EntityManagerFactory e EntityManager, bem parecidas com essas:
http://github.com/caelum/vraptor/tree/master/vraptor-core/src/main/java/br/com/caelum/vraptor/util/jpa

X

Valeu Lucas! Funcionou perfeitamente! :slight_smile:

j0nny

É necessário criar essas classes na mão para o GAE?
Não há um provider padrão já?

Seria legal ter no VRaptor, algo como a anotação @PersistenceContext(unitName) do Spring :smiley:

G

PersistenceContext não é do Spring, mas sim do JPA.

Lucas_Cavalcanti

j0nny, onde colocaria essa anotação @PersistenceContext?

j0nny

Verdade, me confundi…

j0nny

Por exemplo:

@Component
public class ClienteDao {
    
    @PersistenceContext("nomeDaPU")
    private final EntityManager manager;

    public ClienteDao(EntityManager manager) {
        this.manager = manager;
    }

}
Lucas_Cavalcanti

o problema é como criar cada EntityManagerFactory com essa informação…

cria uma issue para isso por favor, linkando pra essa discussão?

j0nny

Lucas Cavalcanti:
o problema é como criar cada EntityManagerFactory com essa informação…

cria uma issue para isso por favor, linkando pra essa discussão?
http://github.com/caelum/vraptor/issues/

Hmm, certo, tbm achei que não seria fácil fazer, mas acho que seria bem útil.

Edit: Issue criada, se fiz algo errado (até pq meu inglês não é dos melhores), me avisem :slight_smile:

Lucas_Cavalcanti

sim, por favor

G

Não vejo na annotation a melhor forma de resolver esse caso.

Acho que o ideal é que no proprio projeto vraptor-gaej tenha as classes necessárias para usar o EntityManager sem precisar usar a annotation. Até porque no GAEJ o nome da PU é sempre o mesmo, transactions-optional, e nunca muda.

j0nny

garcia-jj:
Não vejo na annotation a melhor forma de resolver esse caso.

Acho que o ideal é que no proprio projeto vraptor-gaej tenha as classes necessárias para usar o EntityManager sem precisar usar a annotation. Até porque no GAEJ o nome da PU é sempre o mesmo, transactions-optional, e nunca muda.

Mas e se eu não usar o GAE e não quiser que minha PU se chame default?
E estou em um projeto (não com o VRaptor) em que são necessárias mais de uma PU name, e como faria isso então?
Registrando essas classes para cada PU?

G

Ahh bom. Mas esse tópico é para o GAE/J, hahaha.

Lucas, nesse caso o que você sugere? Que o Vraptor suporte o @PersistenceContext ao invés de injetar no construtor?

j0nny

garcia-jj:
Ahh bom. Mas esse tópico é para o GAE/J, hahaha.

Lucas, nesse caso o que você sugere? Que o Vraptor suporte o @PersistenceContext ao invés de injetar no construtor?

Sim, claro, mas se fizesse algo como o que sugeri, não percisaríamos de classes extras apenas para ter um EntityManager com outra PU, isso se resumiria a tudo.

Lucas_Cavalcanti

se vc vai selecionar o EntityManager por algo que vem na request ou coisa do tipo, vc pode criar uma ComponentFactory que faz a seleção.

se vc vai fazer por anotação, o jeito mais fácil de fazer isso hoje é usando o Guice, e sobrescrevendo um módulo.

nas configurações vc vai colocar

bind(EntityManager.class).annotatedWith(PersistenceUnit("abc")).toProvider(UmaClasseQueCriaEssaPersistenceUnit);
bind(EntityManager.class).annotatedWith(PersistenceUnit("def")).toProvider(UmaClasseQueCriaEssaPersistenceUnit);

tem q ver o jeito direitinho de criar esse annotatedWith, mas é isso

Lucas_Cavalcanti

ainda dá pra injetar no construtor:

public class MeuComponente {
    public MeuComponente(@PersistenceContext("bolinha") EntityManager bolinha) {...}
}
j0nny

Lucas Cavalcanti:
garcia-jj:

Lucas, nesse caso o que você sugere? Que o Vraptor suporte o @PersistenceContext ao invés de injetar no construtor?

ainda dá pra injetar no construtor:

public class MeuComponente {
    public MeuComponente(@PersistenceContext("bolinha") EntityManager bolinha) {...}
}

Exatamente isso…

j0nny

Retomando a questão, por enquanto, vou ter que registrar quais classes todas para eu conseguir usar o VRaptor no GAE/J?
Já foi resolvido algo em questão à essa issue que abri?

Lucas_Cavalcanti

não consegui pensar num jeito bom de implementar a issue. Pelo menos não um que funcione no Spring, Guice e Pico… o @PersistenceContext(“abc”) não vai rolar, não dá pra recuperar esse “abc” no momento da injeção pelos meios comuns.

o jeito mais fácil ainda é com um módulo do guice, e criando anotações ou usando a anotação @Named para decidir qual EntityManager vc vai usar

j0nny

Lucas Cavalcanti:
não consegui pensar num jeito bom de implementar a issue. Pelo menos não um que funcione no Spring, Guice e Pico… o @PersistenceContext(“abc”) não vai rolar, não dá pra recuperar esse “abc” no momento da injeção pelos meios comuns.

o jeito mais fácil ainda é com um módulo do guice, e criando anotações ou usando a anotação @Named para decidir qual EntityManager vc vai usar

Mas isso só funcionaria com o Guice?

G

Lucas, será que dá para fazer alguma coisa com o @Qualifier? Se der melhor, pois o Guice e o Spring suportam a JSR299.

Lucas_Cavalcanti

Modules só funcionaria com o guice…

vc pode criar factories com Qualifiers no Spring também.

não sei como fazer isso com Pico.

Talvez o @Named funcione no Spring tb, se vc usar o javax.inject.Named

Lucas_Cavalcanti

Qualifier/Named talvez funcione, precisamos testar…

não acho que funcione diretamente com ComponentFactories, de qqer forma

j0nny

Neste momento não estou pensando apenas no meu projetinho, que uso Guice, mas sim no VRaptor em geral, que atenderia várias opções.
Por isso a sugestão.

Lucas_Cavalcanti

sim sim… e isso é uma issue aberta há algum tempo já:

a gente só não sabe como resolver direito ainda =(

obrigado pelas sugestões =)

j0nny

Lucas Cavalcanti:
sim sim… e isso é uma issue aberta há algum tempo já:

a gente só não sabe como resolver direito ainda =(

obrigado pelas sugestões =)

Claro, entendo que fácil não é ^^
Mas se houver um jeito de fazer isso sem precisar registrar essas classes, ficaria melhor.

Criado 16 de maio de 2010
Ultima resposta 10 de fev. de 2011
Respostas 33
Participantes 6