Prezados colegas, preciso de ajuda para integrar em uma aplicação web com JSF e EJB 3 os componentesseguintes:
ManagedBean --> Facade --> DAO.
Consegui injetar o DAO diretamente no ManagedBean, o problema é que quando coloquei o Facade na história não consigo fazer funcionar.
Do ManagedBean preciso chamar um método do Facade e do Facade, um método da DAO. Tentei injetar a DAO na Facade e a Facade no ManagedBean, mas o objeto injetado aparece como nulo.
Criei um DAOFactory e injetei no Facade. É possivel injetar o Facade no managedBean?
Estou começando agora a utilizar EJB com JSF e preciso de ajuda. Agradeço se puderem ajudar.
Vou postar a seguir meus componentes ManagedBean, DAOFactory, facade e DAO.
Obrigado
[b]DAOFactoryImpl[/b]
@Stateless
public class DAOFactoryImpl implements DAOFactory {
@PersistenceContext(unitName = "Exemplo001PU")
private EntityManager entityManager;
@Override
public DepartamentoDaoImpl getDepartamentoDao() {
DepartamentoDaoImpl dao = new DepartamentoDaoImpl();
dao.setEntityManager(entityManager);
return dao;
}
}
[b]DepartamentoDaoImpl [/b]
//@Stateless
public class DepartamentoDaoImpl implements DepartamentoDao {
//@PersistenceContext(unitName = "Exemplo001PU")
private EntityManager entityManager;
//private EntityManager entityManager;
public DepartamentoDaoImpl() {
}
@Override
public void create(Departamento a) {
entityManager.persist(a);
}
@Override
public void edit(Departamento entity) {
entityManager.merge(entity);
}
@Override
public void remove(Departamento entity) {
entityManager.remove(entityManager.merge(entity));
}
@Override
public Departamento find(Object id) {
return entityManager.find(Departamento.class, id);
}
@Override
public List<Departamento> buscaTodos() {
CriteriaQuery cq = entityManager.getCriteriaBuilder().createQuery();
cq.select(cq.from(Departamento.class));
return entityManager.createQuery(cq).getResultList();
}
@Override
public List<Departamento> buscarFaixa(int[] faixa) {
CriteriaQuery cq = entityManager.getCriteriaBuilder().createQuery();
cq.select(cq.from(Departamento.class));
Query q = entityManager.createQuery(cq);
q.setMaxResults(faixa[1] - faixa[0]);
q.setFirstResult(faixa[0]);
return q.getResultList();
}
@Override
public int contar() {
CriteriaQuery cq = entityManager.getCriteriaBuilder().createQuery();
Root<Departamento> rt = cq.from(Departamento.class);
cq.select(entityManager.getCriteriaBuilder().count(rt));
Query q = entityManager.createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager=entityManager;
}
}
[b]DepartamentoFacadeImpl[/b]
@Stateless
public class DepartamentoFacadeImpl implements DepartamentoFacade {
@EJB
private DAOFactory daoFactory;
private DepartamentoDao departamentoDao;
public DepartamentoFacadeImpl() {
departamentoDao = daoFactory.getDepartamentoDao();
}
@Override
public void create(Departamento a) {
departamentoDao.create(a);
}
@Override
public void remove(Departamento entity) {
departamentoDao.remove(entity);
}
@Override
public Departamento find(Object id) {
return departamentoDao.find(id);
}
@Override
public List<Departamento> buscaTodos() {
return departamentoDao.buscaTodos();
}
@Override
public List<Departamento> buscarFaixa(int[] faixa) {
return departamentoDao.buscarFaixa(faixa);
}
@Override
public int contar() {
return departamentoDao.contar();
}
@Override
public void edit(Departamento entity) {
departamentoDao.edit(entity);
}
}
[b]DepartamentoMBean[/b]
@ManagedBean(name = "departamentoMBean")
@SessionScoped
public class DepartamentoMBean {
private Departamento corrente;
private DataModel itens = null;
//@EJB
//private dao.DepartamentoDao ejbFacade;
@EJB
private dao.factory.DAOFactoryImpl daoFactory;
private DepartamentoDaoImpl ejbFacade = daoFactory.getDepartamentoDao();
//private DepartamentoFacadeImpl ejbFacade=new DepartamentoFacadeImpl();
private PaginationHelper pagination;
private int indiceItemSelecionado;
public DepartamentoMBean() {
}
public DepartamentoDaoImpl getEjbFacade() {
return ejbFacade;
}
public Departamento getSelecionado() {
if (corrente == null) {
corrente = new Departamento();
indiceItemSelecionado = -1;
}
return corrente;
}
// deixar
public String salvar() {
try {
ejbFacade.create(corrente);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("departamentoCriado"));
corrente = new Departamento();
indiceItemSelecionado = -1;
return "Criar";
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("erroPersistenciaOcorrido"));
return null;
}
}
public String atualizar() {
try {
ejbFacade.edit(corrente);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("departamentoAtualizado"));
return "Ver";
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("erroPersistenciaOcorrido"));
return null;
}
}
public PaginationHelper getPaginacao() {
if (pagination == null) {
pagination = new PaginationHelper(10) {
@Override
public int getContarItens() {
return ejbFacade.contar();
}
@Override
public DataModel criarPageDataModel() {
int vetor[] = new int[]{getPrimeiroItemPagina(), getPrimeiroItemPagina() + getTamanhoPagina()};
List lista = ejbFacade.buscarFaixa(vetor);
ListDataModel lstDataModel = new ListDataModel(lista);
return lstDataModel;
}
};
}
return pagination;
}
public DataModel getItens() {
if (itens == null) {
itens = getPaginacao().criarPageDataModel();
}
return itens;
}
public String proximo() {
getPaginacao().proximaPagina();
itens = null;
return "Listar";
}
public String anterior() {
getPaginacao().paginaAnterior();
itens = null;
return "Listar";
}
public String ver() {
corrente = (Departamento) getItens().getRowData();
indiceItemSelecionado = pagination.getPrimeiroItemPagina() + getItens().getRowIndex();
return "Ver";
}
public String editar() {
corrente = (Departamento) getItens().getRowData();
indiceItemSelecionado = pagination.getPrimeiroItemPagina() + getItens().getRowIndex();
return "Editar";
}
public String remover() {
corrente = (Departamento) getItens().getRowData();
indiceItemSelecionado = pagination.getPrimeiroItemPagina() + getItens().getRowIndex();
acaoExcluir();
itens = null;
return "Listar";
}
public String criar() {
corrente = new Departamento();
indiceItemSelecionado = -1;
return "Criar";
}
public String listar() {
itens = null;
return "Listar";
}
public String excluirVer() {
acaoExcluir();
itens = null;
return "Listar";
}
private void acaoExcluir() {
try {
ejbFacade.remove(corrente);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("departamentoDeletedado"));
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("erroPersistenciaOcorrido"));
}
}
public List getItensDisponiveis() {
return ejbFacade.buscaTodos();
}
}
Bem, encontrei uma solução, mas não foi das melhores…
Por favor, ajudem a encontrar uma solução menos custosa.
Fiz um lookup no managedBean para acessar o FACADE. No Facade eu injetei o DAO e funcionou.
Não entendi, porém, porque tive que fazer um lookup no ManagedBean.
Porque injetando o FACADE no ManagedBean não funcionou?
Eis o lookup no managedBean.
public DepartamentoFacade getEjbFacade() {
try {
InitialContext ic = new InitialContext();
DepartamentoFacade ejbFacade = (DepartamentoFacade) ic.lookup(“facade.DepartamentoFacade”);
return ejbFacade;
} catch (NamingException ex) {
return null;
}
}
Alguém pode me dizer se há uma solução melhor?
O que eu queria era injetar uma DAOFactory na Facade e a Facade no ManagedBean, porém, a DAAOFactory não funcionou, pois o EntityManager dava null.
Enquanto isso vou me virando com essa solução…
Obrigado.
Kra, veja bem…
A injeção de dependências (esses atributos marcados com @PersistenceContext, @EJB, etc…) só funcionam se o objeto em q vc está utilizando a injeção atender a duas condições:
[list]1 - O objeto precisa ser candidado a Injeção. Nem todos os objetos no seu sistema são candidatos a injeção, geralmente os candidatos são EJBs, ManagedBeans do JSF, Servlets, ServletFilters e ServletListeners. Objetos comuns como o seu DepartamentoDaoImpl ñ são candidatos a injeção (a menos q se use CDI, nesse caso todo quase objeto é candidato à injeção);[/list]
[list]2 - O objeto precisa ser instanciado pelo container (servidor). A injeção de dependências ñ é um recurso natural da JVM por isso objetos criados com o operados new ñ tem esse “privilégio”.[/list]
Esse seu problema pode ser resolve com muito menos código. Faça o seu DepartamentoDAOImpl um EJB e injete ele no ManagedBean com a anotação @EJB e pronto. Assim vc dispênça a necessidade de uma fábrica e ainda tira proveito da injeção de dependências no seu DAO. Vc pode, ainda, dispensar o DAO e injetar o EntityManager nos EJBs q possuem a lógica dos casos de uso e p/ evitar q a lógica de acesso aos dados se misture com a lógica do sistema vc pode usar @NamedQueries. O EntityManager é um DAO genérico, ñ há necessidade de vc escrever algo q o framework já te dá pronto. Assim o seu código vai ficar mais simples e mais fácil de manter, se perder flexibilidade, e com CDI vc pode expandir ainda mais essa flexibilidade.
PS.: por favor quanto vc for postar código use tags [code] p/ facilitar a visualização.
Rafael, primeiramente, obrigado pela resposta, foi muito útil, entretanto, surgiram novas dúvidas.
Eu havia feito como você disse, criei o DepartamentoDAOImpl como um EJB e injetei no ManagedBean e funcionou direitinho.
A questão é que tem alguns desenvolvedores que não dispensam um componente FACADE entre o DAO e o ManagedBean.
Quando e porque esse FACADE é necessário, já que ele me parece, na maioria dos exemplos que vi, apenas uma ponte entre esses componentes?
Outra coisa, eu tornei o DAO um EJB e injetei no FACADE. Tornei o FACADE um EJB e tentei injetar no ManagedBean e não funcionou, pois o objeto injetado aparecia como null. Ai eu fiz um lookup no ManagedBean para utilizar o FACADE. Dessa forma funcionou direitinho. Minha dúvida é, porque eu não consegui injetar o FACADE no EJB com a anotação @EJB? Porque eu tive que fazer o lookup?
Se eu fizer como você disse, deixar o DepartamentoDAOImpl como um EJB e injetar no ManagedBean, isso é uma boa prática?
Agradeço pela atenção.
Desculpe por ter postado código de maneira indevida. Na próxima vez utilizarei a tag [code].
Obrigado.
O padrão Service Facade é remanescente do velho J2EE. Originalmente o padrão foi concebido p/ o desenvolvimento aplicações com cliente remoto, como uma aplicação com cliente Swing e EJBs implementando a lógica. Ele é importante nesses casos pq cada chamada de um cliente remoto à um EJB envolve:
[list]serialização dos parâmetros;[/list]
[list]envio pela rede;[/list]
[list]deserialização dos parâmetros no servidor;[/list]
[list]processamento da lógica;[/list]
[list]serialização do retorno;[/list]
[list] transferência do retorno pela rede;[/list]
[list]deserialização do retorno[/list]
Como vc pode notar isso td incorre em um custo alto p/ a performance dessas chamadas, sendo assim, ñ é conveniente o uso de EJBs q possuam uma interface de granularidade fina (com muitos métodos onde cada método faz apenas uma pequena e bem específica taréfa). Os Service Facades oferecem uma interface de granularidade grossa (poucos métodos q fazem muita coisa) pq assim vc reduz o número de chamadas remotas e melhora a performance da aplicação. Por ser esse um modelo muito simples de se seguir, os Service Faces se popularizaram com um padrão p/ todo tipo de aplicação JEE. Isso causa algumas sérias inconveniências pois Service Facades ñ são mais q “bibliotecas de funções” e incentivam a programação estruturada. APIs de granularidade grossa são mais simples de se usar mas são mais difíceis de customizar e reaproveitar. Com a chegada do JEE5, mas principalmente do JEE6 com CDI, muitas ferramentas ajudam vc a evitar esse tipo de padrão quando ele ñ é necessário. Particularmente, quando estou usando JEE6 com CDI em projetos puramente WEB eu prefiro usar meus EJBs (Controllers) como ManagedBeans ao invés de usar dois objetos p/ essa taréfa, lembre-se q o padrão MVC descreve uma arquitetura em 3 camadas e ñ em 4 como é muito comum em aplicativos JEE5 (ou 5 se vc usar DAOs). De fato, os até mesmo os populares DAOs são hoje um pouco redundantes posto q o EntityManager do JPA oferece tds as funcionalidades de um DAOs genérico já pronto p/ vc, escrever DAOs é como reinventar a roda. É claro q p/ q isso funcione na prática vc precisa lançar mão das @NamedQueries através das quais vc pode separar a lógica de acesso ao banco do resto da lógica da aplicação.
Quanto ao problema no seu Facade. Caso ainda esteja interessado em resolve-lo, poste o código p/ q eu possa dar uma olhada.
Rafael, obrigado novamente.
Entendi! Entretanto, tenho mais algumas dúvidas.
Se o EntityManager é um DAO genérico, então não preciso necessariamente de criar componentes DAO.
Sendo assim, a classe que contém métodos como os apresentados a seguir podem ser o meu FACADE (abstrato). Como posso classificar essas classes?
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> buscaTodos() {
CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> buscarFaixa(int[] faixa) {
CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
Query q = getEntityManager().createQuery(cq);
q.setMaxResults(faixa[1] - faixa[0]);
q.setFirstResult(faixa[0]);
return q.getResultList();
}
Mais uma dúvida, os ManagedBeans são CONTROLLERS, as classes com os métodos apresentados acima são representadas como MODEL, certo?
O os componentes como os Converters do JSF são representados na CONTROLLER do MVC?
Muito obrigado pelas respostas.
EJBs são controllers e os models são as suas entidades do JPA. Componentes como Converters ou Validadors do JSF são possuem nenhum papel específico no padrão MVN, embora a taréfa de um Converter é tipicamente realizada por um controller e do Validador por models. Os JEE6 introduzio o framework BeansValidador q permite q as taréfas de validação de dados fiquem realmente nas classes de model (entidades do JPA) e eventualmente nas classes controller (algums casos de uso possuem regras de validação específicas como o campo de confirmação de senha em um cadastro de usuário, a confirmação ñ faz parte do modelo).
Quando uso CDI, minhas views do JSF acessam dirétamente actions em um EJB (no CDI um objeto pode ser, ao mesmo tempo, ManagedBean e EJB) q por sua vez executam a lógica nas entidades JPA. Particularmente a estrutura a seguir me parece um pouco redundante:
// ManagedBean (o código omite getters e setters)
@ManagedBean
public class PersonBean {
@EJB PersonFacade personFacade;
private Person person;
public String save() {
personFacade.save(person);
return "/person/list";
}
}
// ServiceFacade
@Stateless
public class PersonFacade {
@EJB PersonDAO personDAO;
public void save(Person person) {
personDAO.save(person);
}
}
// DAO
@Stateless
public class PersonDAO {
@PersistenceContext EntityManager em;
public void save(Person person) {
em.persist(person);
}
}
Vc tem 3 métodos em 3 classes diferêntes q dizem exatamente a mesma coisa, algumas pessoas podem chamar isso de abstração mas, pra mim, isso é redundância. Bastaria isso:
// (o código omite getters e setters)
@Stateful
@Named
public class PersonCRUD {
@Inject EntityManager em;
private Person person;
public String save() {
em.persist(person);
return "/person/list";
}
}
Desse modo vc escreve menos, entrega resultados mais rápido (garanto q vc vai ganhar pontos com o seu chefe/cliente), tem um código mais simples (+ simples = - bugs & melhor mantenibilidade) e vai ter mais tempo p/ coisas realmente divertidas como namorar ou escutar Iron Maiden :)!
Rafael, muito obrigado.
Você esclareceu todas as minhas dúvidas.
Valeu mesmo.
[]s
Caro Rafael, boa noite.
Estou tentando tornar meu managed bean um EJB. Para isso injetei o EntityManager no managed bean. As opções para consulta e listagem funcionaram, entretanto, nas demais operações como em.persist(corrente) ou em.merge(corrente) retorna uma exceção do tipo TransactionRequiredException.
Você pode dar uma olhada no código. Não consegui achar o erro. Postei o código abaixo.
Obrigado.
package mBean;
import bean.Departamento;
import util.JsfUtil;
import util.PaginationHelper;
import java.util.List;
import java.util.ResourceBundle;
import javax.ejb.Stateless;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
@Stateless
@Named
@ManagedBean(name = "departamentoMBean")
@SessionScoped
public class DepartamentoMBean {
@PersistenceContext(unitName = "Exemplo001PU")
@Inject
EntityManager em;
private Departamento corrente;
private DataModel itens = null;
private DataModel itensSelecionados = null;
private PaginationHelper pagination;
private int indiceItemSelecionado;
public DepartamentoMBean() {
}
public EntityManager getEntityManager() {
return em;
}
public Departamento getSelecionado() {
if (corrente == null) {
corrente = new Departamento();
indiceItemSelecionado = -1;
}
return corrente;
}
public String salvar() {
try {
em.persist(corrente);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("departamentoCriado"));
corrente = new Departamento();
indiceItemSelecionado = -1;
return "Criar";
} catch (Exception e) {
System.out.println(e.getMessage());
String m = e.getMessage();
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("erroPersistenciaOcorrido"));
return null;
}
}
public String atualizar() {
try {
em.merge(corrente);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("departamentoAtualizado"));
return "Ver";
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("erroPersistenciaOcorrido"));
return null;
}
}
public PaginationHelper getPaginacao() {
if (pagination == null) {
pagination = new PaginationHelper(10) {
@Override
public int getContarItens() {
CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
Root rt = cq.from(Departamento.class);
cq.select(em.getCriteriaBuilder().count(rt));
Query q = em.createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
@Override
public DataModel criarPageDataModel() {
int vetor[] = new int[]{getPrimeiroItemPagina(), getPrimeiroItemPagina() + getTamanhoPagina()};
CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
cq.select(cq.from(Departamento.class));
Query q = em.createQuery(cq);
q.setMaxResults(vetor[1] - vetor[0]);
q.setFirstResult(vetor[0]);
List lista = q.getResultList();
ListDataModel lstDataModel = new ListDataModel(lista);
return lstDataModel;
}
};
}
return pagination;
}
public DataModel getItens() {
if (itens == null) {
itens = getPaginacao().criarPageDataModel();
}
return itens;
}
public String proximo() {
getPaginacao().proximaPagina();
itens = null;
return "Listar";
}
public String anterior() {
getPaginacao().paginaAnterior();
itens = null;
return "Listar";
}
public String ver() {
corrente = (Departamento) getItens().getRowData();
indiceItemSelecionado = pagination.getPrimeiroItemPagina() + getItens().getRowIndex();
return "Ver";
}
public String entrarConsulta() {
corrente = new Departamento();
indiceItemSelecionado = -1;
return "Consultar";
}
public String consultar() {
itensSelecionados = null;
return "ListarSelecionados";
}
public DataModel getItensSelecionados() {
if (itensSelecionados == null) {
Query query = em.createNamedQuery("Departamento.findByParteNomDep");
query.setParameter("nomDep", "%" + corrente.getNomDep() + "%");
List lista = query.getResultList();
itensSelecionados = new ListDataModel(lista);
}
return itensSelecionados;
}
public String editar() {
corrente = (Departamento) getItens().getRowData();
indiceItemSelecionado = pagination.getPrimeiroItemPagina() + getItens().getRowIndex();
return "Editar";
}
public String remover() {
corrente = (Departamento) getItens().getRowData();
indiceItemSelecionado = pagination.getPrimeiroItemPagina() + getItens().getRowIndex();
acaoExcluir();
itens = null;
return "Listar";
}
public String criar() {
corrente = new Departamento();
indiceItemSelecionado = -1;
return "Criar";
}
public String listar() {
itens = null;
return "Listar";
}
public String excluirVer() {
acaoExcluir();
itens = null;
return "Listar";
}
private void acaoExcluir() {
try {
em.remove(em.merge(corrente));
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("departamentoDeletedado"));
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/propriedades.recursos.propriedades").getString("erroPersistenciaOcorrido"));
}
}
public List getItensDisponiveis() {
CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
cq.select(cq.from(Departamento.class));
return em.createQuery(cq).getResultList();
}
}
Retomando o tópico…
Tentei fazer algo como o q Dev.Rafael falou, mas n tá rolando…
Tô tentando chamar um método do meu Bean (EJB) direto da minha pagina JSF, mas
n tá indo (só os métodos).
[code] @Named
@Stateless
public class PessoaFacadeBean {
public PessoaFacadeBean(){
setName("Maria 1");
}
private String name;
//Getters and Setters..
public void imprimir() {
System.out.println("Teste");
}
}[/code]
Num ManagedBean tinha o parâmetro “ActionEvent” que n é suportado pelo CDI, então, n to vendo como chamar este método.
Queria algo assim, e outra coisa, qdo anoto meu EJB com algum Scopo (Request por ex), dá pau, e diz q só aceita @Dependent.
Alguém já usou o CDI com EJB 3.1 e FSF, no Eclipse?
Dos exemplos q vi, não funcionaram.
Falew.