Vraptor no GAE/J: Problemas ao usar JPA

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)

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

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

Olá

Isso mesmo. Me confundi aqui.

[]s
Luca

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

Como esse problema foi resolvido? Estou seguindo o sugerido pelo Lucas e mantendo o padrão para o GAE.

[code]<?xml version="1.0" encoding="UTF-8" ?>

<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>

[/code]

[code]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;
}

}[/code]
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)

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

mais pra baixo na stacktrace tem alguma outra exception?

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.

[code]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;
}

}[/code]

E para usar:

[code]public class EchoServlet extends HttpServlet {

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

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

}[/code]

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

[code]<?xml version="1.0" encoding="UTF-8" ?>

<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>

[/code]

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.

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

Valeu Lucas! Funcionou perfeitamente! :slight_smile:

É 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:

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

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

Verdade, me confundi…

Por exemplo:

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

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

}

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

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

[quote=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/[/quote]

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:

sim, por favor

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.

[quote=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.[/quote]

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?