Duvida com EJB 3.0

34 respostas
H

Olá, estou começando a aprender a utilizar o EJB 3.0 ainda, mas estou com um erro que está dificil de solucionar:

Pra começar, estou utilizando a IDE MyEclipse 7.5. Tenho uma tabela no Postgres bem simples chamada User com id, firstname, lastname, datacreated, username e password como atributos.

Primeiramente criei o projeto EJB, após isso fui na tabela (dentro do MyEclipse mesmo) e fiz EJB Reverse Engineering.

Essa engenharia reversa, criou essas classes para mim automaticamente:
AbstractUser
LogUtil
User
UserFacade
UserFacadeLocal
UserFacadeRemote

Até aí tudo bem, para testar se realmente está gravando no banco de dados, criei a classe testeejb2:

public class testeejb2 {

	public static void main(String[] args) {
		UserFacade uf = new UserFacade();
		User u = new User();
		u.setEntityManager(uf.getEntityManager());
		u.setFirstName("joao");
		u.setLastName("silva");
		u.setUsername("jsilva");
		u.setPassword("1234567");
		u.setId(6);
		uf.save(u);
	}

}

Quando dou run pra testar o código aparece esse erro:
[color=red]06/07/2009 13:52:05 com.genuitec.LogUtil log
SEVERE: save failed
java.lang.NullPointerException
at com.genuitec.UserFacade.save(UserFacade.java:45)
at com.genuitec.testeejb2.main(testeejb2.java:14)
Exception in thread "main" java.lang.NullPointerException
at com.genuitec.UserFacade.save(UserFacade.java:45)
at com.genuitec.testeejb2.main(testeejb2.java:14)[/color]

O erro acontece exatamente aqui:
public void save(User entity) {
		LogUtil.log("saving User instance", Level.INFO, null);
		try {
			entityManager.persist(entity); //O ERRO ACONTECE AQUI
			LogUtil.log("save successful", Level.INFO, null);
		} catch (RuntimeException re) {
			LogUtil.log("save failed", Level.SEVERE, re);
			throw re;
		}
	}

Debugando descobri que naquela linha o valor de entityManager vem como null, alguem tem alguma idéia de como resolver? Se vocês acharem necessario posso postar o codigo de outras classes.

Vlw.

34 Respostas

thiago.correa

Você tem que ter um contexto EJB para que seja feita a injeção de dependência, senão o EntityManager vai ser nulo!

H

O que seria exatamente um contexto EJB?

thiago.correa

http://www.google.com.br/search?hl=pt-BR&q=ejb+context&meta=&aq=f&oq=

H

Bom, li varios resultados do google sobre isso, mas ainda fiquei um pouco confuso de como implementaria ele na prática, por acaso seria isso?

@PersistenceContext
	private EntityManager entityManager;

Isso já está na minha classe UserFacade. Ou seria algo diferente?

Esse EJB Context seria uma classe extra? Uma anotação que iria em alguma dessas classes?

Vlw.

gomesrod

O problema é que você não está realmente utilizando EJB, está apenas colocando anotações de EJB em um programa Java SE “stand-alone”.

O que precisa fazer é:

  • Subir um servidor de aplicação e fazer o deploy do seu projeto EJB. (Agora sim, é um EJB pois está dentro do EJB container).
  • No aplicativo teste, usar JNDI para obter a instância do EJB.

Se tiver dúvidas sobre esses procedimentos coloca aí que o pessoal está sempre pronto para ajudar :smiley:

H

Então, eu realmente vi que estava fazendo errado, estava executando como java project.

Agora, usando o JBoss 5.1, faço o deploy do projeto.

A questão agora eh, como q eu “executo” o projeto? Pois eh apenas projeto EJB, tá sem a parte Web, eh possivel testá-lo assim?

Vlw

H

Bom, fiz um projeto exemplo do capitulo 4 do livro Enterprise Javabeans 3.0, o MyEclipse criou pra mim o entity bean cabin e o session bean CabinFacade (como também as interfaces CabinFacadeLocal e CabinFacadeRemote.

Criei também a classe Client

package com.titan;

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;
import javax.rmi.PortableRemoteObject;

public class Client {
    public static void main(String [] args) {
        try {
            Context jndiContext = getInitialContext( );
            Object ref = jndiContext.lookup("CabinFacade/remote");
            CabinFacadeRemote dao = (CabinFacadeRemote)
                PortableRemoteObject.narrow(ref,CabinFacadeRemote.class);
            

            Cabin cabin_1 = new Cabin( );
            cabin_1.setId(1);
            cabin_1.setName("Master Suite");
            cabin_1.setDeckLevel(1);
            cabin_1.setShipId(1);
            cabin_1.setBedCount(3);

            dao.save(cabin_1);

            Cabin cabin_2 = dao.findById(1);
            System.out.println(cabin_2.getName( ));
            System.out.println(cabin_2.getDeckLevel( ));
            System.out.println(cabin_2.getShipId( ));
            System.out.println(cabin_2.getBedCount( ));
            
            Cabin c = new Cabin();
            c = dao.findById(1);
            System.out.println(c.getId() + " " + c.getName() + " " + c.getBedCount() + " " + c.getDeckLevel());

        } catch (javax.naming.NamingException ne){ne.printStackTrace( );}
    }

    public static Context getInitialContext( )
    throws javax.naming.NamingException {

    	Properties p = new Properties( );
    	p.put(Context.INITIAL_CONTEXT_FACTORY,
        	"org.jnp.interfaces.NamingContextFactory");
    	p.put(Context.URL_PKG_PREFIXES,
        	" org.jboss.naming:org.jnp.interfaces");
    	p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
    	return new javax.naming.InitialContext(p);
    }
}

Faço o deploy desse meu projeto, subo o server e dou run as myeclipse server application utilizando o jboss 5.0.

Porém nada é gravado em meu banco de dados, eu gostaria de saber se o meu getInitialContext estaria errado.

E

Cara, não sou nenhum expert, mas, acho que se quer aprender realmente faça o maximo de codigo que puder, sem ficar dependendo tanto de coisas “geradas” por IDE. ;D

H

egamorim, concordo com você, mas já entendi o que é gerado automaticamente pelo myeclipse pelo livro que estou lendo, não tem muito segredo.

Porém desconfio que o erro está no meu Client.

E

Cara, nunca trabalhei muito com EJB, mas pelo que lembro era algo assim:

InitialContext context = new InitialContext();
interface variavel = (interface) context.lookup("NomeDaClasse/remote");

varialvel.metodo();

e no jndi.properties vc diz onde ta o servidor

não sei se ajudou.

H

Bom, mudei pro jeito q você postou:

InitialContext jndiContext = new InitialContext();
        CabinFacadeRemote dao = (CabinFacadeRemote) jndiContext.lookup("CabinFacade/remote");

O autocompletar do dao realmente acha os métodos do CabinFacade, então acredito que está pegando o context corretamente, logo imagino que o erro realmente possa estar no entity bean ou no session bean.

Se alguem puder dar uma olhada por cima dessas minhas 2 classes, elas estão aqui.

O CabinFacade coloquei aqui apenas o método save, pq senão iria ficar muita coisa.

import java.util.List;
import java.util.logging.Level;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * Facade for entity Cabin.
 * 
 * @see com.titan.Cabin
 * @author MyEclipse Persistence Tools
 */
@Stateless
public class CabinFacade implements CabinFacadeRemote {
	// property constants
	public static final String SHIP_ID = "shipId";
	public static final String BED_COUNT = "bedCount";
	public static final String NAME = "name";
	public static final String DECK_LEVEL = "deckLevel";

	@PersistenceContext
	private EntityManager entityManager;

	/**
	 * Perform an initial save of a previously unsaved Cabin entity. All
	 * subsequent persist actions of this entity should use the #update()
	 * method.
	 * 
	 * @param entity
	 *            Cabin entity to persist
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void save(Cabin entity) {
		LogUtil.log("saving Cabin instance", Level.INFO, null);
		try {
			entityManager.persist(entity);
			LogUtil.log("save successful", Level.INFO, null);
		} catch (RuntimeException re) {
			LogUtil.log("save failed", Level.SEVERE, re);
			throw re;
		}
	}
}

O método entity bean:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Cabin entity. @author MyEclipse Persistence Tools
 */
@Entity
@Table(name = "cabin", schema = "public")
public class Cabin implements java.io.Serializable {

	// Fields

	private Integer id;
	private Integer shipId;
	private Integer bedCount;
	private String name;
	private Integer deckLevel;

	// Constructors

	/** default constructor */
	public Cabin() {
	}

	/** minimal constructor */
	public Cabin(Integer id) {
		this.id = id;
	}

	/** full constructor */
	public Cabin(Integer id, Integer shipId, Integer bedCount, String name,
			Integer deckLevel) {
		this.id = id;
		this.shipId = shipId;
		this.bedCount = bedCount;
		this.name = name;
		this.deckLevel = deckLevel;
	}

	// Property accessors
	@Id
	@Column(name = "id", unique = true, nullable = false)
	public Integer getId() {
		return this.id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name = "ship_id")
	public Integer getShipId() {
		return this.shipId;
	}

	public void setShipId(Integer shipId) {
		this.shipId = shipId;
	}

	@Column(name = "bed_count")
	public Integer getBedCount() {
		return this.bedCount;
	}

	public void setBedCount(Integer bedCount) {
		this.bedCount = bedCount;
	}

	@Column(name = "name", length = 30)
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Column(name = "deck_level")
	public Integer getDeckLevel() {
		return this.deckLevel;
	}

	public void setDeckLevel(Integer deckLevel) {
		this.deckLevel = deckLevel;
	}

}
E

e o jndi.properties ?

H

Bom, depois q eu postei reparei q vc falou do jndi.properties, eu vi em um outro tutorial (do jboss as) que ele deveria ficar dentro do diretório src e deveria conter:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099

É isso mesmo? Se for, também não resolveu. =\

E

tenta assim :

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://localhost:1099

H

Durante o deploy ocorrem esses erros, não sei se tem a ver:

DEPLOYMENTS MISSING DEPENDENCIES:
  Deployment "jboss.j2ee:jar=Titan Cruises 4.jar,name=CabinFacade,service=EJB3" is missing the following dependencies:
    Dependency "<UNKNOWN jboss.j2ee:jar=Titan Cruises 4.jar,name=CabinFacade,service=EJB3>" (should be in state "Described", but is actually in state "** UNRESOLVED Demands 'persistence.unit:unitName=#titan' **")
  Deployment "jboss.j2ee:jar=Titan Cruises 4.jar,name=CabinFacade,service=EJB3_endpoint" is missing the following dependencies:
    Dependency "jboss.j2ee:jar=Titan Cruises 4.jar,name=CabinFacade,service=EJB3" (should be in state "Configured", but is actually in state "PreInstall")
  Deployment "persistence.unit:unitName=#AplicacaoEJBPU" is missing the following dependencies:
    Dependency "jboss.jca:name=App,service=DataSourceBinding" (should be in state "Create", but is actually in state "** NOT FOUND Depends on 'jboss.jca:name=App,service=DataSourceBinding' **")
  Deployment "persistence.unit:unitName=#titan" is missing the following dependencies:
    Dependency "jboss.jca:name=TitanDB,service=DataSourceBinding" (should be in state "Create", but is actually in state "** NOT FOUND Depends on 'jboss.jca:name=TitanDB,service=DataSourceBinding' **")

DEPLOYMENTS IN ERROR:
  Deployment "jboss.jca:name=TitanDB,service=DataSourceBinding" is in error due to the following reason(s): ** NOT FOUND Depends on 'jboss.jca:name=TitanDB,service=DataSourceBinding' **
  Deployment "<UNKNOWN jboss.j2ee:jar=Titan Cruises 4.jar,name=CabinFacade,service=EJB3>" is in error due to the following reason(s): ** UNRESOLVED Demands 'persistence.unit:unitName=#titan' **
  Deployment "jboss.jca:name=App,service=DataSourceBinding" is in error due to the following reason(s): ** NOT FOUND Depends on 'jboss.jca:name=App,service=DataSourceBinding' **
Além disso, o meu persistence.xml tá aqui, talvez o fato de não salvar tenha a ver com ele:
<?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="titan" transaction-type="JTA">
  		<jta-data-source>java:/TitanDB</jta-data-source>
	</persistence-unit>
  
</persistence>
E

falta mapear:

<persistence-unit name="titan" transaction-type="JTA">  
	<jta-data-source>java:/TitanDB</jta-data-source>
	<class>NomeDaClasseCompletoIncluindoOPacote</class>
</persistence-unit>
H

Que classe coloco alí? O entity bean?
E se sim, o entity bean Cabin que está dentro do pacote com.titan, fica desse jeito?

<class>com.titan.Cabin</class>

E

isso, me parece certo …

H

Bom, assim também não foi, já não sei mais o que pode estar errado. =\

Só por via das duvidas, só em dar o run as myeclipse server application ele já deveria “rodar” o programa e gravar no banco de dados né?

E

deveria …

o erro continua o mesmo ?
como e onde estão suas configurações do banco ? (dialeto, usuario, senha )

H

egamorim:
deveria …

o erro continua o mesmo ?
como e onde estão suas configurações do banco ? (dialeto, usuario, senha )

Os erros no jboss continuam os mesmos.

As minhas configurações de banco acredito estarem corretas, já as testei com projeto hibernate.

Eu seleciono o postgres logo quando crio o projeto, porém aparentemente não é criado nenhum arquivo com as configurações do meu banco (tal como o hibernate.cfg.xml para projetos hibernate).

E

Cara, naquela hora me esqueci de tocar num detalhe, o seu persistence.xml tem algo parecido com isso :

<property name="hibernate.dialect" value="dialeto"/>
			<property name="hibernate.connection.driver_class" value="class"/>
			<property name="hibernate.connection.url" value="banco"/>
			<property name="hibernate.connection.username" value="usuario"/>
			<property name="hibernate.connection.password" value="senha"/>

?

H

Não tem não, isso vai logo nas primeiras linhas do persistence.xml?

E
<persistence-unit name="titan" transaction-type="JTA">    
    <jta-data-source>java:/TitanDB</jta-data-source>  
    <class>NomeDaClasseCompletoIncluindoOPacote</class>  
    <properties>
              <property name="hibernate.dialect" value="dialeto"/>  
              <property name="hibernate.connection.driver_class" value="class"/>  
              <property name="hibernate.connection.url" value="banco"/>  
              <property name="hibernate.connection.username" value="usuario"/>  
              <property name="hibernate.connection.password" value="senha"/>
    </properties>
</persistence-unit>

é o que iria no xml do hibernate

H

Agora o meu persistence.xml está assim:

<?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="titan" transaction-type="JTA">      
      <jta-data-source>java:/TitanDB</jta-data-source>    
      <class>com.titan.Cabin</class>    
      <properties>  
                <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>    
                <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>    
                <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/postgres"/>    
                <property name="hibernate.connection.username" value="postgres"/>    
               <property name="hibernate.connection.password" value="*****"/>  
      </properties>  
  </persistence-unit>   
  
</persistence>

Porem, ainda não está funcionando.

E

Cara, vou ver se consigo achar aqui algum exemplo de projeto pra te passar.

H

Opa, ajudaria bastante, a maioria dos projetos ejb prontos que acho são do ejb 2.x

E vlw por toda a ajuda até agora.

H

Ah, e outra duvida que me surgiu.

No persistence.xml o jta-data-source seria o nome do meu banco de dados? Eu tenho colocado java:/postgres geralmente (postgres é o nome da conexão com o banco de dados que eu criei no myeclipse), eu creio que o erro possa estar aí.
Pesquisei um pouco e li que seria o nome dado ao data source no application server, mas como que eu dou esse nome no JBoss?.

H

Mais 1 coisa, seguindo um outro tutorial que usava glassfish, ele adicionou as libs do application server ao projeto, no caso do JBoss devo fazer isso também?

Olhei no diretório do JBoss e realmente há um sub-diretório libs, se isso é necessário, apenas não sei qual das libs devo adicionar.

E

passa seu msn que eu te mando um exemplo bem simples

IronJavaMan

Cara olha só eu também estou estudando EJB3, pelo que eu pude acompanhar neste tópico, eu não vi vc mencionando que criou seu DataSource no JBoss, pois vc deve criar o deploy com o Data Source na instância do Servidor. Tente isso, vamos ver se dar certo.

H

Bom, eu fiz esse datasource hoje, não sei se corretamente.

Fui em Administration Console->Resources->Datasources->Local TX Datasource->Add new resource.

Select template: default
jndi name: postgres
username e password coloquei os mesmos do postgresql
jdbc driver class: org.postgresql.Driver
conection url: jdbc:postgresql://localhost:5432/postgres

No resto deixei o padrão e cliquei em save.

E o meu msn é [email removido]

H

Bom, mexi em bastante coisa aqui, os erros que restaram são esses:

--- MBeans waiting for other MBeans ---
ObjectName: persistence.units:jar=OnlyEJB.jar,unitName=OnlyEJBPU
  State: FAILED
  Reason: javax.persistence.PersistenceException: [PersistenceUnit: OnlyEJBPU] class or package not found
  I Depend On:
    jboss.jca:service=DataSourceBinding,name=PostgresDS
  Depends On Me:
    jboss.j2ee:jar=OnlyEJB.jar,name=PessoafisicaFacade,service=EJB3

ObjectName: jboss.j2ee:jar=OnlyEJB.jar,name=PessoafisicaFacade,service=EJB3
  State: NOTYETINSTALLED
  I Depend On:
    persistence.units:jar=OnlyEJB.jar,unitName=OnlyEJBPU

ObjectName: persistence.units:jar=ejb2.jar,unitName=ejb2PU
  State: NOTYETINSTALLED
  I Depend On:
    jboss.jca:name=postgres,service=DataSourceBinding

--- MBEANS THAT ARE THE ROOT CAUSE OF THE PROBLEM ---
ObjectName: persistence.units:jar=OnlyEJB.jar,unitName=OnlyEJBPU
  State: FAILED
  Reason: javax.persistence.PersistenceException: [PersistenceUnit: OnlyEJBPU] class or package not found
  I Depend On:
    jboss.jca:service=DataSourceBinding,name=PostgresDS
  Depends On Me:
    jboss.j2ee:jar=OnlyEJB.jar,name=PessoafisicaFacade,service=EJB3

O datasource tá certo já, o persistence xml está do jeito que postei anteriormente neste mesmo topico.
Googleei um pouco já e ainda não encontrei alguma solução, será que esses erros seriam por alguma configuração no JBoss que ainda devo fazer?

H

Problema resolvido. :slight_smile:

O que estava faltando aqui era adicionar o driver do postgres na pastar default/lib do JBoss, agora está funcionando como deve. ^^

Valeu a todos que me ajudaram.

Criado 6 de julho de 2009
Ultima resposta 24 de jul. de 2009
Respostas 34
Participantes 5