Exemplo de JSF+JPA
Fala galera!
Eu Bruno Rafael venho aqui neste para lhes mostrar um exemplo básico para quem tem interesse em começar a desenvolver aplicações web com JSF e JPA.
Então vamos lá!
Criei uma aplicação simples, sem nenhum framework adicional alem de JSF (1.2_04) e JPA (toplink), estou rodando com Java 6 e Tomcat 6.
Os unicos jars no classpath da aplicação são:
o faces-config.xml que ficou assim:
A entidade a ser persistida (fiz apenas com uma para o exemplo):
e o Managed Bean do exemplo:
E tudo deve estar funcionando agora.
Quer dizer, quase …
O Tomcat 6 tem um zip de nome annotations-api.jar, que contem uma versão baleada das anotações do JPA, então editei este zip, removi o diretório persistence de dentro dele, e movi os jars do toplink para o diretório lib do tomcat, isto fez o exemplo funcionar.
Para os que acharem que isto é trabalho demais, podemos mudar um pouco a abordagem também, criamos um InjectionProvider para o JSF, com o código a seguir (Eu copiei o código do SVN do Java Server Faces RI e adicionei o suporte direto a JPA):
Eu Bruno Rafael venho aqui neste para lhes mostrar um exemplo básico para quem tem interesse em começar a desenvolver aplicações web com JSF e JPA.
Então vamos lá!
Criei uma aplicação simples, sem nenhum framework adicional alem de JSF (1.2_04) e JPA (toplink), estou rodando com Java 6 e Tomcat 6.
Os unicos jars no classpath da aplicação são:
- jsf-impl.jar
- jsf-api.jar
- jstl.jar
- toplink-essentials-agent.jar
- toplink-essentials.jar
<?xml version=”1.0″ encoding=”UTF-8″?>Ou seja, apenas adicionei o servlet do JSF.
<web-app id=”WebApp_ID” version=”2.4″ xmlns=”http://java.sun.com/xml/ns/j2ee” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd”>
<display-name>jsfjpa</display-name>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
o faces-config.xml que ficou assim:
<?xml version=”1.0″ encoding=”UTF-8″?>Ou seja, tem um ManagedBean e um navigation rule apenas.
<!DOCTYPE faces-config PUBLIC
“-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN”
“http://java.sun.com/dtd/web-facesconfig_1_1.dtd”>
<faces-config>
<navigation-rule>
<navigation-case>
<from-outcome>home</from-outcome>
<to-view-id>/home.jsp</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
<managed-bean>
<managed-bean-name>exemplo</managed-bean-name>
<managed-bean-class>br.com.urubatan.jsfjpa.mbean.ExemploMBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>id</property-name>
<property-class>java.lang.Long</property-class>
<value>#{param.id}</value>
</managed-property>
</managed-bean>
</faces-config>
A entidade a ser persistida (fiz apenas com uma para o exemplo):
package br.com.urubatan.jsfjpa.data; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Cliente { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }uma entidade simples, com o minimo de anotações possivel.
e o Managed Bean do exemplo:
package br.com.urubatan.jsfjpa.mbean; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.PersistenceContext; import br.com.urubatan.jsfjpa.data.Cliente; public class ExemploMBean { @PersistenceContext(name = "jsfjpa2") private EntityManager em; private List<Cliente> clientes; private Cliente cliente; private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public List<Cliente> getClientes() { return clientes; } public void setClientes(List<Cliente> clientes) { this.clientes = clientes; } public Cliente getCliente() { return cliente; } public void setCliente(Cliente cliente) { this.cliente = cliente; } @PostConstruct public void init() { if (id != null) cliente = em.find(Cliente.class, id); else cliente = new Cliente(); list(); } @SuppressWarnings("unchecked") private void list() { clientes = em.createQuery("select c from Cliente c").getResultList(); } @PreDestroy public void destroy() { } public String save() { EntityTransaction t = em.getTransaction(); t.begin(); if (cliente.getId() == null) { em.persist(cliente); } else { em.merge(cliente); } t.commit(); return "home"; } public String delete() { EntityTransaction t = em.getTransaction(); t.begin(); if (cliente.getId() != null) { Cliente c = em.getReference(Cliente.class, cliente.getId()); em.remove(c); } t.commit(); return "home"; } }Para que isto funcione é preciso registrar no tomcat um “Resource” que sera uma factory de EntityManager, eu fiz isto utilizando um arquivo “context.xml” no diretório META-INF da aplicação, pois achei mais fácil de portar para o exemplo, o context.xml ficou assim:
<Context>E a classe PersistenceManagerObjectFactory, fica mais ou menos assim:
<Resource auth=”Container” factory=”br.com.urubatan.jsfjpa.util.PersistenceManagerObjectFactory” name=”jsfjpa2″ type=”javax.persistence.EntityManagerFactory”
unitName=”jsfjpa” />
</Context>
package br.com.urubatan.jsfjpa.util; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import javax.naming.Context; import javax.naming.Name; import javax.naming.RefAddr; import javax.naming.spi.ObjectFactory; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.apache.naming.ResourceRef; public class PersistenceManagerObjectFactory implements ObjectFactory { private String unitName; private static Map factoryMap = new HashMap(); public String getUnitName() { return unitName; } public void setUnitName(String unitName) { this.unitName = unitName; } @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { RefAddr un = ((ResourceRef) obj).get("unitName"); EntityManagerFactory emf = getFactory((String) un.getContent()); return emf.createEntityManager(); } private EntityManagerFactory getFactory(String unitName) { EntityManagerFactory factory = (EntityManagerFactory) factoryMap.get(unitName); if (factory == null) { factory = Persistence.createEntityManagerFactory(unitName); factoryMap.put(unitName, factory); } return factory; } }Eu utilizei genérics no código que esta no zip do exemplo, mas precisei remover aqui do código no blog por que o editor do wordpress não se da muito bem com < no código.
E tudo deve estar funcionando agora.
Quer dizer, quase …
O Tomcat 6 tem um zip de nome annotations-api.jar, que contem uma versão baleada das anotações do JPA, então editei este zip, removi o diretório persistence de dentro dele, e movi os jars do toplink para o diretório lib do tomcat, isto fez o exemplo funcionar.
Para os que acharem que isto é trabalho demais, podemos mudar um pouco a abordagem também, criamos um InjectionProvider para o JSF, com o código a seguir (Eu copiei o código do SVN do Java Server Faces RI e adicionei o suporte direto a JPA):
package br.com.urubatan.jsfjpa.util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.naming.NamingException; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.PersistenceContext; import javax.servlet.ServletContext; import org.apache.AnnotationProcessor; import com.sun.faces.spi.InjectionProvider; import com.sun.faces.spi.InjectionProviderException; public class Tomcat6JPAInjectionProvider implements InjectionProvider { private final ServletContext servletContext; private static Map<String, EntityManagerFactory> factoryMap = new HashMap<String, EntityManagerFactory>(); public Tomcat6JPAInjectionProvider(ServletContext servletContext) { this.servletContext = servletContext; } public void inject(Object managedBean) throws InjectionProviderException { try { getProcessor().processAnnotations(managedBean); } catch (Exception e) { if (!(e instanceof NamingException)) throw new InjectionProviderException(e); } for (Field f : managedBean.getClass().getDeclaredFields()) { if (f.isAnnotationPresent(PersistenceContext.class)) { EntityManager em = getEntityManager(f.getAnnotation(PersistenceContext.class)); } } for (Method m : managedBean.getClass().getDeclaredMethods()) { if (m.isAnnotationPresent(PersistenceContext.class)) { } } } private EntityManager getEntityManager(PersistenceContext annotation) { return null; } private EntityManagerFactory getFactory(String unitName) { EntityManagerFactory factory = factoryMap.get(unitName); if (factory == null) { factory = Persistence.createEntityManagerFactory(unitName); factoryMap.put(unitName, factory); } return factory; } public void invokePreDestroy(Object managedBean) throws InjectionProviderException { try { getProcessor().preDestroy(managedBean); } catch (Exception e) { throw new InjectionProviderException(e); } } public void invokePostConstruct(Object managedBean) throws InjectionProviderException { try { getProcessor().postConstruct(managedBean); } catch (Exception e) { throw new InjectionProviderException(e); } } private AnnotationProcessor getProcessor() { return ((AnnotationProcessor) servletContext.getAttribute(AnnotationProcessor.class.getName())); } }E adicionamos um parametro no web.xml que informa ao JSF que deve utilizar este provider em vez do default assim:
<context-param>
<param-name>com.sun.faces.injectionProvider</param-name>
<param-value>br.com.urubatan.jsfjpa.util.Tomcat6JPAInjectionProvider</param-value>
</context-param>
depois disto é necessário alterar o managedbean, a anotação @PersistenceContext, deve utilizar o parametro unitName para especificar qual a persistence unit que utilizar, e não o name, ja que o primeiro é o nome da persistence unit, o segundo é o nome JNDI a ser utilizado.
Utilizando esta segunda abordagem, ainda é possivel alterar alguns parametros de comportamento, como por exemplo, pegar o nome do campo, quando o unitName não for informado, mas isto vou deixar para vocês brincarem um pouquinho …
Com isto temos com bem pouco código, um cadastro simples utilizando JSF e JPA, com injeção de dependencias, uma grande flexibilidade na forma de trabalhar, e uma dica importante, de como acessar páginas utilizando GET com JSF em vez de POST, como parece ser a unica forma a primeira vista.
O que acharam desta forma de programar, utilizando DI e apenas seguindo as especificações e padrões Java EE, e ainda assim sem a necessidade de um container Java EE completo

Comentários
Postar um comentário