Paginacao Por Demanda com JSF Parte 4
Janeiro 22, 2009
Complementando o post anterior, como solicitado em um comentário.
O rich:dataTable e o rich:datascroller deverão ficar da seguinte forma na pagina.
<rich:dataTable id=”TabelaUsuarios” value=”#{usuarioBean.dataModel}”
binding=”#{usuarioBean.dataTable}” var=”usuario” rows=”10″>
<f:facet name=”header” />
<f:facet name=”footer”>
<rich:datascroller for=”TabelaUsuarios” stepControls=”hide” align=”center” />
</f:facet>
</rich:dataTable>
Caso seja necessário guardar o estado da tabela, poderá ser utilizado o tomahawk para isso. Basta fazer:
<t:saveState value=”#{usuarioBean.dataTable}”></t:saveState>
Paginação por Demanda com JSF – Parte 3
Dezembro 28, 2008
Note que os métodos são genéricos, mas que isso só funcionária se a query nunca tivesse nenhum tipo de restrição, ou seja, se você quisesse realizar o count apenas de determinadas informações, dessa maneira seria impossível e você teria que reescrever o método count sempre que precisasse restringir alguma informação.
Pensando nisso vamos abstrair mais ainda o Método count. Nele agora será necessário informar às restrições que deverão compor a query.
Vejamos como ficará o método count.
public Integer count(List<Criterion> restricoes){
Integer size = 0;
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());
if(restricoes != null){
for (Criterion criterion : restricoes) {
criteria.add(criterion);
}
}
criteria.setProjection(Projections.rowCount());
size = (Integer) criteria.uniqueResult();
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate().close();
}
return size;
}
Note que agora é solicitado uma Lista de restrições (Criterions) que serão utilizados pela criteria. Isso torna o método genérico a qualquer consulta, sendo necessário passar a restrição que desejar dentro de uma lista, da seguinte maneira.
List lista = new ArrayList();
lista.add(Restrictions.eq(“usuario”, usuario.GetUsuario()));
Dessa maneira é informado qual restrição quero para o método count.
Pensando dessa forma o método listByCriteriaDemanda deverá permitir restrições, ordenações e até mesmo projeções. Aqui entramos em um problema:
Teremos que prevenir o LIE (Lazy Initialization Exception). Se a sua entidade possuir uma listagem e está será utilizada na tela, a mesma deverá ser Lazy false ou você terá que realizar a prevenção do LIE. No meu caso teremos que inicializar essa dependência.
Mas como? Meu DAO é genérico, não tenho acesso aos atributos da classe e não sei se esta classe possui ou não listagens que poderão causar o LIE.
Vamos utilizar um recurso do Java. Vamos utilizar reflexão, ou seja, vamos descobrir se a classe possui ou não atributos que são listas e iremos inicializar esses atributos.
Vamos ver como ficaria o nosso método após a adição das restrições e inicialização de objetos de um determinado tipo por reflexão.
public List<T> listByCriteriaDemanda(Integer startPage, Integer maxPage, List<Criterion> restricoes, List<Projection> projecoes, List<Order> ordenacao){
List<T> list = new ArrayList<T>();
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());
if(restricoes !=null){
for(Criterion r: restricoes){
criteria.add(r);
}
}
if(projecoes != null){
for (Projection p : projecoes) {
criteria.setProjection(p);
}
}
if(ordenacao != null){
for(Order order: ordenacao){
criteria.addOrder(order);
}
}
criteria.setFirstResult(startPage);
criteria.setMaxResults(maxPage);
list = criteria.list();
for (T t : list) {
for (Method method : t.getClass().getMethods()) {
if(method.getName().indexOf("get") > -1){
Object obj = method.invoke(t);
if(obj instanceof java.util.Collection){
for (T lista: (List<T>)obj) {
Hibernate.initialize(lista);
}
}
}
}
}
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate.close();
}
return list;
}
Nosso método agora ficou bastante genérico, trouxemos as restrições, as projeções e as ordenações para dentro dele. Na nossa classe agora poderemos criar nossas restrições, projeções e ordenação e manter nosso DAO genérico, sem precisar implementar novamente as funcionalidades da classe.
O trecho abaixo é referente a parte de reflexão que iremos utilizar para descobrir se um atributo é uma coleção ou não, para podermos inicializar este atributo.
for (T t : list) {
for (Method method : t.getClass().getMethods()) {
if(method.getName().indexOf("get") > -1){
Object obj = method.invoke(t);
if(obj instanceof java.util.Collection){
for (T lista: (List<T>)obj) {
Hibernate.initialize(lista);
}
}
}
}
}
Aqui nos fazemos uma iteração na lista criada pela criteria. Criamos um objeto do tipo Method, esse objeto é um Array e é carregado a partir do método
t.getClass.getMethods();
Esse método retorna um Methods[].
Feito isso nós testamos se no nome do método existe a palavra “get”, pois será necessário invocar esse get para sabermos qual o tipo de objeto será retornado.
O método indexOf retorna >-1 caso seja verdadeiro, ou seja, se a palavra get existir no nome do método então o retorno será verdadeiro.
Com isso nós invocamos o método através do method.invoke(t);
Como não sabemos qual o tipo de retorno então criamos um objeto do tipo Object que irá receber o retorno. Se esse objeto for filho de collection (List, Set, ArrayList, HashTree, etc) então iremos iterar sobre esse objeto e iremos inicializar item após item da listagem resultante. Por padrão, os relacionamentos OneToMany retornam um objeto do tipo PersistenceBag que é filho de Collection.
Feito isso sua classe está completamente inicializada e não haverá problemas com LazyInitialization e você terá uma paginação em demanda funcional.
Em breve estarei disponibilizando um projeto war contendo esse DAO, SuperDAO e as classes necessárias para realizar a paginação. Um exemplo funcional com acesso a uma base de dados.
Paginação por Demanda com JSF – Parte 2
Dezembro 28, 2008
O get do DataModel terá que ter a seguinte aparência:
public DataModel getDataModel() {
int totalListSize = dao.count();
List<Usuário> pagedList = dao.listByCriteriaDemanda(getDataTable().getFirst(), getDataTable().getRows());
dataModel = new PagedDataModel(pagedList, tatalListSize);
return dataModel;
}
Explicando o que cada um faz:
O primeiro carinha (totalListSize) é responsável por realizar uma consulta no banco que irá retornar a quantidade de linhas existentes para aquela consulta.
O segundo é responsável por realizar a consulta por demanda via hibernate, utilizando criteria para isso.
O terceiro seria a instancia do dataModel de acordo com a classe PagedDataModel que criamos.
Vamos criar os dois métodos, o primeiro para contar as linhas da tabela e o segundo para listar de acordo com a quantidade desejada. Sempre utilizando criteria.
No meu caso possuo uma classe chamada SuperDao que é genérica.
O método getEntidade retorna um objeto do tipo T e o método getHibernate me retorna o objeto Hibernate que está sendo utilizado.
public List<T> listByCriteriaDemanda(Integer startPage, Integer maxPage){
List<T> list = new ArrayList<T>();
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());
criteria.setFirstResult(startPage);
criteria.setMaxResults(maxPage);
list = criteria.list();
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate.close();
}
return list;
}
public Integer count(){
Integer size = 0;
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());
criteria.setProjection(Projections.rowCount());
size = (Integer) criteria.uniqueResult();
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate().close();
}
return size;
}
Paginação por Demanda com JSF – Parte 1
Dezembro 28, 2008
Um dos grandes problemas de aplicações grande é o fato da Paginação em memória. Imaginem uma aplicação que trabalha com milhões de dados e que possui milhões de acesso simultâneo como o Ebay ou Mercado Livre, seria muito complicado lidar com milhões de informações na tela ou mesmo realizar uma paginação em memória.
PAGINAÇÃO EM DEMANDA
Há poucos dias comecei a pesquisar sobre esse tipo de paginação, devido aos fatos que expus acima. Nossa aplicação começou pequena, com poucas informações. Tudo corria muito bem até que a aplicação começou realmente a ser utilizada. No nosso caso esta aplicação serviria para controle de documentação acadêmica para um determinado evento que estava sendo organizado. Em principio nosso sistema reagiu bem, com a paginação em memória tudo ficou muito bonito, os filtros funcionavam e as informações eram exibidas com relativa velocidade.
Nossa aplicação era constituída de 4 módulos:
O primeiro era o módulo de cadastro de usuários, onde eles iriam informar dados básicos.
O segundo era um módulo de gerenciamento dos usuários.
O terceiro era um módulo de gerenciamento de atividades do usuário para o evento. (cadastro de material informativo, de resumos para apresentações, etc)
O quarto era um módulo administrativo para gerenciar as atividades de todos os usuários (aprovação cadastral, aprovação de trabalhos para apresentação, lotação de trabalhos, etc).
Os 3 primeiros módulos funcionaram corretamente, sendo que o 4 módulo foi o crucial para queremos “reescrever” a aplicação inteira. Este quarto modulo se tornou um peso enorme para a aplicação e para nossos servidores. Devido a grande massa de dados e a quantidade enorme de pessoas utilizando o sistema simultaneamente, nosso servidor sempre ficava indisponível, devido a paginação em memória.
Imaginem 100 pessoas acessando simultaneamente sua aplicação e solicitando 10 mil registros a cada clique do mouse? Isso daria em torno de 1 milhão de informações sendo carregadas simultaneamente na memória do computador.
Esse foi o motivo que me fez pensar em “paginação por demanda”. Já tinha ouvido falar deste tipo de paginação mas nunca tinha me deparado com uma situação real onde fosse necessário utilizá-la. Bom… deixando de lado esse “histórico” vamos ao que interessa: Paginação em Demanda.
Neste tipo de paginação que irei demonstrar é necessário utilizar-se de alguns componentes.
Vamos lá.
Primeiro é necessário criar uma classe que extenda o DataModel.
Essa classe terá que ter a seguinte aparência:
import java.util.List;
import javax.faces.model.DataModel;
public class PagedDataModel extends DataModel{
private int rowIndex = -1;
private int totalNumRows;
private int pageSize;
private List list;
public PagedListDataModel() {
super();
}
public PagedListDataModel(List list, int totalNumRows) {
super();
setWrappedData(list);
this.totalNumRows = totalNumRows;
this.pageSize = list.size();
}
public boolean isRowAvailable() {
if(list == null)
return false;
int rowIndex = getRowIndex();
if(rowIndex >=0 && rowIndex < list.size())
return true;
else
return false;
}
public int getRowCount() {
return totalNumRows;
}
public Object getRowData() {
if(list == null)
return null;
else if(!isRowAvailable())
throw new IllegalArgumentException();
else {
int dataIndex = getRowIndex();
return list.get(dataIndex);
}
}
public int getRowIndex() {
return (rowIndex % pageSize);
}
public void setRowIndex(int rowIndex) {
this.rowIndex = rowIndex;
}
public Object getWrappedData() {
return list;
}
public void setWrappedData(Object list) {
this.list = (List) list;
}
}
Em alguns exemplos encontrados o construtor dessa classe possui 3 argumentos, a lista que será utilizada pelo dataModel, o número total de linhas que a tua consulta retornou e o número total de linhas que serão exibidos a cada página.
No meu exemplo o ultimo argumento foi retirado, pois por algum motivo a ultima página nunca era exibida. Digamos, minha paginação será de 10 em 10, possuo no banco 1903 registros, terei 190 paginas com 10 registros e uma página contendo 3 registros, essa página nunca era exibida. Testando percebi que se eu preenchesse os 10 valores com objetos vazios a página 191 era exibida. Para corrigir isso informo ao dataModel que o size da página será o size da lista.
Esse size é o “rows” que o dataTable possui, na paginação em memória setamos que seria 10 rows, na paginação em demanda esse valor se torna dinâmico.
Criado essa classe será necessário trazer alguns objetos para o teu Bean.
O primeiro seria um objeto do tipo DataModel e o segundo seria um HtmlDataTable, com seus get’s e set’s
O import do HtmlDataTable deverá ser igual ao componente que será utilizado na tela, se na tela você utilizar um h:dataTable então o import terá que ser o HtmlDataTable do JSF, se o componente for um rich:dataTable então o import terá que ser o HtmlDataTable do RichFaces.
Paginação em Memória com JSF
Dezembro 27, 2008
A paginação em memória é realmente muito simples com o JSF e ajuda muito quando a página não contém muitas informações. Normalmente essa página é o espelho de um Managed Bean onde o mesmo possui um atributo referente a sua listagem.
Managed Bean UsuarioBean:
public class UsuarioBean {
private Usuario usuario = new Usuario();
private List<Usuario> listUsuario = new ArrayList<Usuario>();
private UsuarioBO usuarioBO = new UsuarioBO();
private String retorno;
public String list(){
listUsuario = usuarioBO.findAll();
retorno = "showListUsuario";
return retorno;
}
public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
public List<Usuario> getListUsuario() {
return listUsuario;
}
public void setListUsuario(List<Usuario> listUsuario) {
this.listUsuario = listUsuario;
}
public UsuarioBO getUsuarioBO() {
return usuarioBO;
}
public void setUsuarioBO(UsuarioBO usuarioBO) {
this.usuarioBO = usuarioBO;
}
}
faces-config.xml
<managed-bean>
<description>Bean de Usuários do Sistema</description>
<managed-bean-name>usuarioBean</managed-bean-name>
<managed-bean-class>br.com.view.UsuarioBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Note que o nosso Managed Bean tem escopo de Sessão, sendo todas as informações guardadas na sessão do usuário no momento em que houver uma requisição deste Bean. Esse também é um problema para aplicações de grande porte, quanto mais Beans de sessão sua aplicação possuir mais memória seu servidor terá que alocar para manter as informações e se o tempo de sessão de cada usuário estiver muito longo poderá ocorrer um leak de memória e logo seu servidor ficará fora do ar.
A intenção é ter o máximo de Beans com escopo de requisição e manter informações necessárias através do a4j:keepAlive ou do t:saveState, o primeiro faz parte do pacote a4j encontrado no richFaces e o segundo faz parte do projeto Tomahawk da apache. Os dois conseguem manter um escopo maior que requisição e menor que sessão, o chamado escopo de “conversação” que o JBoss Seam possui. Isso faz com que algumas informações necessárias permaneçam vivas após a requisição ter sido encerrada.
PAGINAÇÃO EM MEMÓRIA
Para fazermos a paginação em memória utilizaremos o método list do Managed Bean para localizar todos os registros, colocar dentro de uma lista que está no escopo da classe e acessarmos na página diretamente em um DataTable ou DataGrid da seguinte forma:
<rich:dataTable id="tabelaUsuario" rows="10" var="c" value="#{usuarioBean.listUsuario}">
<f:facet name="header">
<rich:columnGroup>
<rich:column>
<h: outputText value="Código" />
</rich:column>
<rich:column>
<h: outputText value="Usuário" />
</rich:column>
</rich:columnGroup>
</f:facet>
<rich:column filterBy="#{c.id}" filterEvent="onkeyup">
<f:facet name="header">
<h: outputText value=" " />
</f:facet>
<h: outputText value="#{c.id}" />
</rich:column>
<rich:column filterBy="#{c.usuario}" filterEvent="onkeyup">
<f:facet name="header">
<h: outputText value=" " />
</f:facet>
<h: outputText value="#{c.usuario}" />
</rich:column>
</rich:dataTable>
<rich:datascroller for="tabelaUsuario" maxPages="10" stepControls="hide" />
Note que o dataTable possui algumas informações especificas: A primeira seria a listagem que será utilizada (localizada no atributo value do dataTable), a segunda seria a variável que dará acesso aos atributos da lista, ou seja, o “item da vez”.
Destaquei em negrito alguns atributos interessantes.
Primeiro o filterBy do rich:column. Este atributo informa que, nesta coluna, existirá um filtro. Esse filtro é exibido como uma caixinha de texto logo abaixo do nome da coluna, para fazermos isso é necessário criar o objeto “facet” sendo o header da coluno e dentro desse facet é necessário que tenha um outputText onde o usuário poderá digitar o que desejar localizar.
Isto é um recurso muito interessante mas que funciona muito bem com a paginação em memória, pois todos os itens estão carregados na memória naquele instante e poderão ser “localizados” com o digitar do usuário. Explicando melhor: O usuário ao digitar uma palavra esse filtro irá começar a filtrar a listagem que se encontra na sessão do usuário naquele determinado momento, trazendo apenas as informações digitadas.
Na paginação por demanda isso se torna mais complicado, pois a listagem não está toda na memória, apenas parte dela. Para isso o usuário terá que criar um evento que irá buscar a informação a cada tecla que o usuário digitar, isso poderá acarretar em leak de memória novamente pois, dependendo da quantidade de informações dentro do BD, cada tecla digitada será realizado uma consulta no banco de dados.
Segundo está o nosso paginador padrão do RichFaces, o datascroller. Esse é o responsável por realizar a paginação em memória, nele informamos quantas páginas serão exibidas em seu paginador.
Esse seria um exemplo bem comum para a paginação em memória.
Ciclo de Vida JSF
Dezembro 13, 2008
O JSF possui 6 fases no seu ciclo de vidas. Muitos programadores não compreendem o ciclo de vida do JSF, implentando um código se algumas funcionalidades.
As fases do ciclo de vida do JSF são:
- Restore View (recuperar a tela)
- Apply request values (Aplicar valores do request)
- Process Validation (Processo de Validação)
- Update model values (Atualização dos valores no Modelo)
- Invoke Application (Invocando a aplicação)
- Render response (retornar resposta)
Fase 1 – Restore View
Nesta fase o request vem atravês do controller do FacesContext. O Controller examina o request e extrai o ID da visão (tela, view) que é determinado pelo nome da página JSP.
O Framework do JSF utiliza esse ID para localizar os componentes para a tela atual. Se a visão ainda não existe ela será criada pelo framework, se ela já existir será apenas reutilizada pelo controller. A visão possui os componentes GUI’s (Graphical User Interface).
Esta fase possui 3 instâncias: nova visão, visão inicial e o retorno (postback) da visão. Cada instância dessa é tratada de forma diferente.
Na nova visão, o JSF constroi a visão para o Faces e vincula esta página aos handlers e validators. A visão é salva no FacesContext como um objeto. O FacesContext contêm todas as informações de como se deverá manipular este GUI para a requisição corrente. O FacesContext guarda as visões na propriedade ViewRoot o qual possui todos os componentes da visão atual.
Na visão inicial, o JSF cria uma visão em branco. Esta visão será populada de acordo com o evento solicitado pelo usuário. Da visão inicial o JSF avança diretamente para a fase de Render Response.
No caso do postback (o usuário retornou a página que acessou anteriormente), a visão já existe sendo necessário apenas ser restaurada.
Fase 2 – Apply Request values
O propósito desta fase é fazer com que cada componente recupere se estado corrente. O Componente deve primeiro ser criado ou recuperado a partir do FacesContext, seguido por seus valores. Os valores dos componentes são geralmente recuperados dos parametros de request, no entanto eles podem ser recuperados dos cabeçalhos ou cookies gerados.
Se um componente não está com seu atributo Immediate setado para True, o valor será apenas convertido. Se o campo for Inteiro, o valor será convertido para um inteiro. Se a conversão falhar será adicionado no FacesContext uma mensagem de erro que será exibida durante a fase de Render response.
Se um componente estiver com seu atributo immediate setado para true o valor será convertido para o tipo especifico do atributo e logo após será validado, o valor convertido será guardado no componente.
É nesta fase onde ocorrem as conversões de valores, ou seja, onde os Converter’s criado pelo usuário serão utilizados ou os conversores padrão do JSF.
Fase 3 – Process Validation
É nesta fase onde ocorrem as validações. Esta validação pode ser criada pelo desenvolvedor ou “obtida” diretamente do JSF. Os valores são validados de acordo com as regras de validação da aplicação. Se um valor estiver errado é gerado uma mensagem de erro e adicionada no FacesContext e o componente é marcado como inválido. Se algum componente estiver marcado como inválido o JSF avançará automaticamente para a fase de Render Response que irá exibir o erro de validação.
Se não houver nenhum erro de validação o JSF irá avançar para a fase de Update Model values.
Fase 4 – Update Model Values
Esta é a fase onde é atualizado o valor no lado do servidor, atualizando a propriedade dentro do seu Backing Bean. Somente as propriedades que estão sendo manipulados do componente é que serão atualizadas. Note que esta fase só irá acontecer após a fase de validação, o que lhe garante que os dados que estão indo para o seu Bean são válidos a nível de “tela” pois os mesmos poderão ser inválidos a nível de regras de negócio de sua aplicação.
Fase 5 – Invoke Application
Nesta fase o Controller do JSF invoca a aplicação para manipular o envio do form. O valor do componente sempre estará convertido, validado e aplicado aos objetos de modelo, então vc poderá utilizá-los para realizar suas regras de negócio.
Nesta fase vc especifica um “outcome” ou valor de saída que irá servir para continuar o fluxo lógico da aplicação. Você faz isso definindo uma “saída” quando houver sucesso no envio de um form e irá dar um retorno desta saída. Por exemplo, uma saída “sucesso” irá levar o usuário para a próxima página. Mas para essa navegação poder acontecer é necessário que seja criado um mapeamento dentro do arquivo faces-config.xml com este “outcome”.
Fase 6 – Render Response
Esta fase irá exibir a tela com todos os componentes em seu estado atual.
Retirado de: http://www-128.ibm.com/developerworks/library/j-jsf2/
Prevenindo LazyInitializationException
Dezembro 13, 2008
Um dos problemas do programador é o LazyInitilizationExecption, isso ocorre quando um objeto precisa de uma conexão e ela não está mais disponível, provocando um erro de inicialização.
Note que não estou utilizando nenhum tipo de Controle de Transação por frameworks, ou seja, o controle de transação é todo feito dentro do próprio método.
Digamos que nossa aplicação possui um entidade chamada Cliente e outra entidade chamada Telefone, onde um cliente pode possuir vários telefones.
vamos as entidades:
@Entity
@Table(name = "cliente")
public class Cliente implements Serializable{
private static final long serialVersionUID = -3494651140532522766L;
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String nomeCliente;
@OneToMany(mappedBy = "cliente", targetEntity = Telefones.class)
@Cascade(value = CascadeType.ALL)
private List telefones = new ArrayList();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNomeCliente() {
return nomeCliente;
}
public void setNomeCliente(String nomeCliente) {
this.nomeCliente = nomeCliente;
}
public List getTelefones() {
return telefones;
}
public void setTelefones(List telefones) {
this.telefones = telefones;
}
}
@Entity
@Table(name="telefone")
public class Telefones implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String telefone;
@ManyToOne
@JoinColumn(name = "cliente_id")
private Cliente cliente = new Cliente();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
}
==========================================================
Bom, tenho duas entidades que se relacionam, na Entidade Cliente tenho um mapeamento OneToMany que está mapeado por “cliente” na entidade Telefones. Na Entidade Telefones tenho um relacionamento ManyToOne para a entidade Cliente.
Note que foi definido um Cascade na entidade Cliente, ou seja, nossa aplicação irá salvar o Cliente e o telefone. Por padrão todos os relacionamentos são definidos como Lazy (preguiçoso) e isso gera o LIE.
Note também que nossas entidades foram instanciadas, não está sendo feito uma inversão de controle até mesmo pq esse não é o tema do Post.
Bom… como vamos prevenir o LIE nesse caso, existem algumas soluções e fica a critério do programador definir qual a melhor solução para a sua aplicação.
Solução Nº 1.
A primeira solução e mais facil seria colocar o atributo: @LazyCollection(LazyCollectionOption.FALSE)
Isso iria fazer com que o relacionamento não fosse mais Lazy, ou seja, sempre que eu precisar de um cliente ele iria trazer todos os Telefones.
Essa solução implica em um problema sério. Imagine uma aplicação onde existem vários relacionamentos, digamos, uma aplicação de vendas onde um cliente possui uma compra, uma compra possui vários produtos, pra cada produto exista um fornecedor, pra cada fornecedor exista um endereço.
Nesta situação, ao recuperar uma venda seria feito uma query no banco buscando por este cliente, com esta query todos os objetos do banco (cliente, produto, fornecedor, endereço, etc) iriam ser inicializados naquele momento. Isso gera um problema quando a sua aplicação fica grande, quanto mais informações vc tem no BD mais tempo será necessário para poder realizar tal query. Esta solução, com o tempo, iria matar a sua aplicação.
Solução Nº 2:
Alguns programadores gostam de criar um filtro para abrir e fechar a conexão, prevenindo o LIE dessa maneira pois a conexão estaria aberta enquanto o objeto fosse necessário. Está seria uma solução mais “elegante” porém vc estaria preso a este filtro.
Solução Nº 3:
Você poderá inicializar os objetos que deseja manualmente.
Peguemos o exemplo do cliente / telefones, vamos fazer uma query para trazer apenas o cliente com o ID 1, então temos o seguinte:
public Cliente findById(Long id){
Cliente cliente = null;
try {
getHibernate().getSession().beginTransaction();
cliente = (Cliente) getHibernate().getSession().
get(Cliente.class, id);
for(Telefones tel: cliente.getTelefones()){
Hibernate.initialize(tel);
}
getHibernate().getSession().
getTransaction().commit();
} catch (Exception e) {
getHibernate().getSession().getTransaction().rollback();
} finally {
getHibernate().getSession().close();
}
return cliente;
}
Note que o Hibernate.initialize está inicializando cada objeto do tipo Telefones.
Eu particularmente prefiro este método pois consigo controlar a transação.
Basicamente é assim que podemos resolver o problema do LIE, em alguns frameworks mais recentes como o JBoss Seam existe um escopo maior que request e menor que sessão, um escopo de conversação que faz com que o contexto fique ativo até o final de todo o processo. Isso previne o problema do LIE pois o contexto ainda estaria vivo, ou seja, a conexão ainda estaria ativa no momento em que fosse solicitado para exibir os dados da listagem de telefones.
Java
Dezembro 13, 2008
Erro no Deploy de uma aplicação Seam no JBoss:
JSF1029: The specified InjectionProvider implementation ‘org.jboss.web.jsf.integration.injection.JBossInjectionProvider’ does not implement the InjectionProvider interface.
Correção:
Setar o valor para True no atributo useJBossWebLoader dentro do arquivo
$JBOSS_HOME\server\default\jboss-web.deployer\META-INF\jboss-service.xml – No Jboss AS 4.2.3GA
No Jboss AS 5.0 o arquivo encontra-se em:
$JBOSS_HOME\server\default\deployers\jbossweb.deployer\META-INF\war-deployers-jboss-beans.xml
Aumentar Tempo de Start do JBoss no Eclipse:
Ganymede
Abrir a aba de server’s, clicar duas vezes em cima do server JBoss. Irá aparecer a tela de configurações do server. No lado direito terá Timeouts, clique e irá aparecer a opção de aumentar o tempo de start do server.
Europa
Windows>Preferences>Server>Server Timeout delay
Erro no deploy de aplicações JSF no JBoss:
Exception sending context initialized event to listener instance of class org.jboss.web.jsf.integration.config.JBossJSFConfigureListener
Correção:
Retire os Jar’s do JSF e JSF-impl de dentro da aplicação, coloque-os na pasta Cliente do JBoss AS.
No JBoss 5.0 a versão do jar do Facelets deverá ser a Facelets 1.1.15B1.jar ou a versão que vem junto com JBoss Seam mais recente. Caso utilize uma versão antiga o JBoss não irá renderizar o Arquivo e lançará uma exception na tela.
Erro
[hibernate] Error executing macro: outputValue
[hibernate] required parameter: property is not specified.
[hibernate] The problematic instruction:
[hibernate] ———-
[hibernate] ==> macro outputValue [on line 38, column 1 in util/TypeInfo.ftl]
[hibernate] in user-directive outputValue [on line 100, column 17 in view/view.xhtml.ftl]
Correção
Editar os arquivos:
view.xhtml.ftl
linha 100
Palavra identifier está escrito como indentifier
edit.xhtml.ftl
linha 127
Palavra identifier está escrito como indentifier
Alterar as palavras para identifier
Estes arquivos estão localizados em:
$SEAM_HOME\seam-gen\view