O Entity Bean é um componente da especificação JEE que representa as entidades do sistema, ou seja, a grosso modo é um registro de uma tabela em um banco de dados. Ele armazena permanentemente os dados em uma estrutura de dados secundária, que na maioria das vezes é um banco de dados. Um Entity Bean modela realmente os dados de negócio, e cada instância de um Entity seria exatamente um registro na base de dados.

Ao invés de manipularmos os dados diretamente do banco, utilizamos os Entities para realizar esta tarefa, com a vantagem do Servidor de Aplicações controlar todos os serviços middleware.

Existem 2 tipos de Entity Bean:BMP e CMP.

  • BMP (Bean Managed Persistent): neste primeiro tipo, a responsabilidade pelo controle da persistência é feita pelo programador e todo o controle fica diretamente no Bean.
  • CMP (Container Managed Persistent): neste tipo de EJB, a persistência é realizada pelo Container JEE, onde o único trabalho do desenvolvedor é configurar como a persistência vai ser realizada.

Ao longo do artigo iremos apenas falar sobre o Entity Bean CMP, onde mostraremos ujm exemplo completo de como implementar, bem como a parte de configuração do Servidor de Aplicações e um cliente web chamando-o.

Entity Bean CMP (Container Managed Persistence)

Em um Entity Bean tipo CMP, é o Container que realiza a persistência, ele sabe quais campos precisam ser persistidos, também faz todo o mapeamento com o banco de dados e decide quais registros precisam ser incluídos, alterados ou excluídos. Algumas pessoas acham mais simples utilizar esta tecnologia, pois fica a cargo do servidor realizar essas operações de persistência, enquanto o desenvolvedor se preocupa em focar na lógica de negócio do sistema.

Podemos confirgurar os Entities beans para executar a geração automática de tabelas bem como criação, remoção e atualização de tabelas durante o deploy. Também é responsável por inserir, alterar, excluir e consultar dados do banco que representam os registros. Existem alguns métodos prontos para serem usados na API, como por exemplo: ejbCreate(), ejbStore(), ejbRemove() etc. Estes métodos ajudam e também aceleram o desenvolvimento e confiabilidade de sua aplicação.

CMP é muito utilizado em aplicações onde existem requisitos de independência de banco de dados ou aplicação que terão que executar em ambientes distribuídos. É uma boa opção para quem desenvolve aplicações totalmente baseadas em JEE, pois é um dos componentes mais completos da especificação, pelo fato de utilizar varias tecnologias tais como JNDI, JTA, JPA, conceito de datasource, muitas funcionalidades declarativas em XML, dentre outras. Como vantagem podemos citar:

  • Gerência de transação automática;
  • Suporte a transações distribuídas;
  • Pool de objetos;
  • Clustering;
  • Container gerencia ciclo de vida e ativação do Bean;
  • Portabilidade entre fabricantes de EJB Server;
  • Segue por completo a especificação J2EE, que é um padrão de Mercado para Java Web;
  • O desenvolvedor pode controlar ou não o que irá acontecer nos métodos de sincronização;
  • Com CMP o programador não precisa utilizar APIs como por exemplo JDBC;
  • Escalabidade e integração com RMI e CORBA.

Além de todas as vantagens já citadas acima, o CMP ainda tem: concorrência e sincronismo de acesso, cache de dados do banco utilizado em memória e inicialização inteligente (lazy e easy load).

Os Entities têm algumas operações básicas de consulta como busca pela chave primária, porém para consultas mais complexas e específicas dos objetos persistidos, poderá usar uma linguagem de consulta conhecida como EQL (EJB Query Language).

Substituindo SQL por consultas em XML, apresentando a EJB-QL

A EJB-QL (Enterprise JavaBeans Query Language) é a linguagem SQL para os Entity Beans e semelhante a SQL, mas com pequenas diferenças. Ela é implementada de forma declarativa, ou seja, suas queries são escritas dentro do descritor ejb-jar.xml. Através de métodos "finder" e "select" declarados na interface Home do Bean, o servidor automaticamente lê a query definida no descritor e basicamente traduz essa query para SQL da base de dados que está sendo usada.

Com todo esse processo apresentado, ganhamos mais um outro quesito importante e uma vantagem para aplicação que seria a transportabilidade de base de dados.

Para a EJB-QL funcionar precisamos criar um esquema abstrato na declaração do Entity, partir desse esquema abstrato que definimos os relacionamentos do Bean, as queries EJB-QL e a definição dos campos. Ele é abstrato justamente para não confundir com o esquema lógico implementado na base de dados. O conceito de esquema abstrato é bem simples como veremos em exemplo abaixo:

 
1. <abstract-schema-name>Objeto</abstract-schema-name> 

2. <query>

3.   <query-method>

4.     <method-name>findAll</method-name>

5.     <method-params />

6.   </query-method>

7.   <ejb-ql>select object(o) from Objeto o</ejb-ql>

8. </query>
            

Na linha 1 temos a declaração do esquema abstrato.

Nas linhas de 2 a 8 a implementação de uma query simples EJB-QL, faz uma listagem de todos os objetos da tabela objeto que referência-se com o método findAll definido na interface Home do Bean Objeto.

A declaração da query é muito simples, onde selecionamos o objeto declarado no esquema abstrato damos um alias para ele. A partir desse alias ele pode manipular todos os campos persistidos no banco de dados. A intenção é mostrar um exemplo simples com a linguagem EJB-QL, mas ela é muito poderosa e tem muitos outros recursos que não vão ser explorados neste artigo.

  1. Classe Bean: classe abstrata onde implementa a classe javax.ejb.EntityBean.
  2. Classe PrimaryKey: classe serializada que representa a chave primária do Bean.
  3. Interface Home: interface que extende da classe javax.ejb.EjbHome.
  4. Interface Remote: interface que extenda da classe javax.ejb.EjbObject.
  5. ejb-jar.xml: descritor para declarar o Entity e suas transações.
  6. jboss.xml: descritor para declarar o jndi name do Bean.
  7. jbosscmp-jdbc.xml: descritor para fazer o mapeamento do Bean com o banco de dados.
  8. um cliente para acesso ao EJB, exemplo: acesso web com um Servlet.
Observação: é necessário configurar o servidor de aplicações juntamente com o banco de dados a ser usado. No nosso exemplo vamos usar o Mysql.

Configurando servidor JBoss com banco de dados Mysql

Primeiramente, iremos configurar o banco de dados a ser usado no exemplo. Será utilizado o banco Mysql, e precisaremos configurar alguns arquivos tais como:

  • %JBOSS_HOME%\server\default\conf\standardjaws.xml: colocar a seguinte declaração dentro da tag <jaws>:
    
    <datasource>java:/MySqlDS</datasource>
    
    <type-mapping>mySQL</type-mapping>
    
    <debug>false</debug>
                     
  • %JBOSS_HOME%\server\default\conf\standardjbosscmp-jdbc.xml: colocar a seguinte declaração dentro das tags <jbosscmp-jdbc> e <defaults>:
     
    <datasource>java:/MySqlDS</datasource>
    
    <datasource-mapping>mySQL</datasource-mapping>
                    
  • %JBOSS_HOME%\server\default\deploy\mysql-ds.xml: declarar o arquivo da seguinte maneira:
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    <datasources>
    
      <local-tx-datasource>
    
        <jndi-name>MySqlDS</jndi-name>
    
        <connection-url>jdbc:mysql://localhost/basededados</connection-url>
    
        <driver-class>com.mysql.jdbc.Driver</driver-class>
    
        <user-name>root</user-name>
    
        <password>root</password>
    
        <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.
    
          MySQLExceptionSorter</exception-sorter-class-name>
    
        <metadata>
    
           <type-mapping>mySQL</type-mapping>
    
        </metadata>
    
      </local-tx-datasource>
    
    </datasources>
                     
Observação: Não podemos esquecer de colocar o driver do mysql em %JBOSS_HOME%\server\default\lib.

Desenvolvendo um exemplo de Entity Bean passo a passo no Jboss

Nesta etapa vamos criar um modulo ejb com os arquivos e estruturas necessárias, adicionando um exemplo simples de um Entity chamado Objeto, com a funcionalidade de criação. Também adicionaremos um exemplo de consulta com EJB-QL. Vamos escrever suas principais classes nos devidos pacotes e logo após as declarações necessárias para o funcionamento correto.


// ObjetoEntityBean.java

package br.com.otavio.ejb.cmp;

 

import java.rmi.RemoteException;

import javax.ejb.CreateException;

import javax.ejb.EJBException;

import javax.ejb.EntityBean;

import javax.ejb.EntityContext;

 

/**

 * Objeto EntityBean com os métodos gets/sets representando os atributos,

 * gerenciados pelo container (CMP). Aqui também são encontrados os métodos

 * call-back do container.

 *

 * @author Otávio Henrique Vieira Sanchez

 */

 

public abstract class ObjetoEntityBean implements EntityBean {

 

         private EntityContext entityContext = null;

 

         public abstract int getCodigo();

         public abstract void setCodigo(int id);

         public abstract String getDescricao();

         public abstract void setDescricao(String descricao);

 

         public void setEntityContext(EntityContext ec) throws EJBException, RemoteException {

                   this.entityContext = ec;

         }

         public void unsetEntityContext() throws EJBException, RemoteException {

                   this.entityContext = null;

         }

         public ObjetoPK ejbCreate(int id) throws CreateException {

                   this.setCodigo(id);

                   return null;

         }

         public void ejbPostCreate(int id) throws CreateException {}

         public void ejbRemove() throws EJBException, RemoteException {}

         public void ejbActivate() throws EJBException, RemoteException {}

         public void ejbPassivate() throws EJBException, RemoteException {}

         public void ejbLoad() throws EJBException, RemoteException {}

         public void ejbStore() throws EJBException, RemoteException {}

 

}

 

// ObjetoHome.java

package br.com.otavio.ejb.cmp;

 

import javax.ejb.EJBHome;

import javax.ejb.CreateException;

import java.rmi.RemoteException;

import javax.ejb.FinderException;

import java.util.Collection;

 

/**

 * Classe que representa a interface home do bean.

 * São declarados os métodos de sincronização com o banco de dados.

 *

 * @author Otávio Henrique Vieira Sanchez

 */

public interface ObjetoHome extends EJBHome {

   

   public ObjetoRemote create(int id) throws CreateException, RemoteException;

   public Collection findAll() throws FinderException, RemoteException;

   public ObjetoRemote findByPrimaryKey(ObjetoPK pk) throws FinderException, RemoteException;

 

}

 

// ObjetoRemote.java

package br.com.otavio.ejb.cmp;

 

import java.rmi.RemoteException;

import javax.ejb.EJBObject;

 

/**

 * Classe que representa a interface remota do bean.

 * São declarados os métodos gets/sets dos atributos do banco.

 *

 * @author Otávio Henrique Vieira Sanchez

 */

public interface ObjetoRemote extends EJBObject {

   

   public String getDescricao() throws RemoteException;

   public int getCodigo() throws RemoteException;

   public void setCodigo(int id) throws RemoteException;

   public void setDescricao(String descricao) throws RemoteException;

  

}

 

// ObjetoPK.java

package br.com.otavio.ejb.cmp;

 

import java.io.Serializable;

 

/**

 * Classe que representa a chave primária do bean.

 *

 * @author Otávio Henrique Vieira Sanchez

 */

public class ObjetoPK implements Serializable{

         public int codigo;

 

         public ObjetoPK(int id){

                   this.codigo = id;

         }

 

         public ObjetoPK(){}

 

         public int hashCode(){

                   Integer i = new Integer(codigo);

                   return i.hashCode();

         }

 

         public boolean equals(Object o){

                   if(o instanceof ObjetoPK)   {

                            Integer i = new Integer(codigo);

                            Integer oi = new Integer( ((ObjetoPK)o).codigo);

                            return i.equals( oi );

                   }

                   else

                            return false;

         }

 

         public String toString(){

                   return Integer.toString(codigo);

         }

        

}

 

// ejb-jar.xml

<?xml version="1.0"?>

 

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"

                     "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

 

<ejb-jar>

 

 <enterprise-beans>

   <entity>

      <description><![CDATA[ObjetoEntityBean]]></description>

      <ejb-name>ObjetoEJB</ejb-name>

      <home>br.com.otavio.ejb.cmp.ObjetoHome</home>

      <remote>br.com.otavio.ejb.cmp.ObjetoRemote</remote>

      <ejb-class>br.com.otavio.ejb.cmp.ObjetoEntityBean</ejb-class>

      <persistence-type>Container</persistence-type>

      <prim-key-class>br.com.otavio.ejb.cmp.ObjetoPK</prim-key-class>

      <reentrant>false</reentrant>

      <cmp-version>2.x</cmp-version>

      <abstract-schema-name>ObjetoASN</abstract-schema-name>

      <cmp-field >

         <description><![CDATA[]]></description>

         <field-name>descricao</field-name>

      </cmp-field>

      <cmp-field >

         <description><![CDATA[]]></description>

         <field-name>codigo</field-name>

      </cmp-field>

      <query>

         <query-method>

            <method-name>findAll</method-name>

            <method-params/>

         </query-method>

         <ejb-ql><![CDATA[SELECT OBJECT(o) FROM ObjetoASN AS o]]></ejb-ql>

      </query>     

   </entity>    

</enterprise-beans>

 

 <assembly-descriptor>

  <container-transaction>

     <method>

        <ejb-name>ObjetoEJB</ejb-name>

        <method-name>*</method-name>

     </method>

     <trans-attribute>Required</trans-attribute>

   </container-transaction>

 </assembly-descriptor>

 

 </ejb-jar>

 

// jboss.xml

<?xml version="1.0" ?>

<jboss>

         <enterprise-beans>

        <entity>

           <ejb-name>ObjetoEJB</ejb-name>

           <jndi-name>ObjetoJNDI</jndi-name>

        </entity>      

         </enterprise-beans>

        

</jboss>

 

//jbosscmp-jdbc.xml

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

<!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 3.0//EN" "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_0.dtd">

 

<jbosscmp-jdbc>

   <defaults>

                   <datasource>java:/MySqlDS</datasource>

                    <datasource-mapping>mySQL</datasource-mapping>

                    <create-table>true</create-table>

         <preferred-relation-mapping>relation-table</preferred-relation-mapping>

   </defaults>

 

   <enterprise-beans>

      <entity>

         <ejb-name>ObjetoEJB</ejb-name>

         <table-name>objeto</table-name>

         <cmp-field>

            <field-name>codigo</field-name>

            <column-name>codigo</column-name>

         </cmp-field>

         <cmp-field>

            <field-name>descricao</field-name>

            <column-name>descricao</column-name>

         </cmp-field>

      </entity>     

   </enterprise-beans>



</jbosscmp-jdbc>
             

Chamando algumas funções do EJB CMP através de um cliente WEB

Concluído todos os passos para a criação e deploy do Entity CMP, agora chamaremos o nosso Bean através de um Servlet no próprio servidor de aplicações Jboss. Lembrando que para executar um Servlet é necessário um servidor web, mas neste caso estamos usando o Jboss que já vem com o Tomcat instalado por default, então mãos a obra.

Assim, basta apenas criar um projeto web e “linkar” ele com o projeto ejb para acessar as classes necessárias. Abaixo vamos escrever por fim o Servlet e a declaração no web.xml.

 
// ServletClientEJB

 

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Collection;

import java.util.Iterator;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import br.com.otavio.ejb.cmp.ObjetoHome;

import br.com.otavio.ejb.cmp.ObjetoRemote;

import br.com.otavio.ejb.cmp.ServiceLocator;

 

public class ServletClientEJB extends HttpServlet {

 

 

  private ObjetoHome home;

  private PrintWriter out;

 

  public void doGet(HttpServletRequest request, HttpServletResponse response)

         throws IOException, ServletException{

        

             response.setContentType("text/html");

             out = response.getWriter();

             out.println("<HTML>");

             out.println( "<HEAD>");

             out.println(   "<TITLE> Teste</TITLE>");

             out.println( "</HEAD>");

             out.println( "<BODY>");

 

             try{

                             

                       out.println(   "Inserindo EJB ...");

 

                       for (int i=1; i<10; i++){

                            String descricao = new String("EJB Entity CMP "+ i);

                           criarRegistro(i, descricao);

                       }

 

                       out.println("<br><br><br>");

                       out.println("Consultando itens inseridos:<br><br>");

 

            Collection all = home.findAll();

 

            for (Iterator iterator = all.iterator(); iterator.hasNext();) {

                   ObjetoRemote o = (ObjetoRemote) iterator.next();

                       out.println(o.getCodigo() +" - "+ o.getDescricao() +"<br>");

            }

                 

         }catch (Exception e) {

                            // TODO: handle exception

                            e.printStackTrace();

                   }

      

             out.println( "</BODY>");

             out.println("</HTML>");

  }

 

  private void criarRegistro(int id, String descricao) throws Exception{

      try {

           

         home = (ObjetoHome) ServiceLocator.getInstance().getHome("ObjetoJNDI", ObjetoHome.class);                         

         ObjetoRemote remote = home.create(id);

         remote.setDescricao(descricao);            

        

      } catch (Exception e) {

          e.printStackTrace();

      }   

  }

 

 }

 

// web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

 

<!DOCTYPE web-app

  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

 

<web-app>

 

    <distributable/>

 

    <servlet>

        <servlet-name>ServletClientEJB</servlet-name>

        <servlet-class>ServletClientEJB</servlet-class>

    </servlet>

 

    <servlet-mapping>

        <servlet-name>ServletClientEJB</servlet-name>

        <url-pattern>/ServletClientEJB</url-pattern>

    </servlet-mapping>

 

</web-app>
            

Considerações finais

O objetivo principal deste artigo é mostrar ao desenvolvedor, uma tecnologia muito importante dentro da especificação JEE, onde é possível ter o total controle de sua base de dados, deixando as tarefas mais triviais e até complexas a cargo do servidor, possibilitando que o desenvolvedor se dedique mais na regra de negócio da aplicação.

O artigo explica como fazer a implementação de um EJB Entity Bean CMP simples, demonstrando que ele pode ser usado em aplicações de grande e médio porte sem problemas.