Introdução ao Hibernate Search

Veja neste artigo o que é o Hibernate Search e como podemos criar uma aplicação com este framework.

Hibernate Search permite aos desenvolvedores utilizarem facilmente o Apache Lucene com nosso modelo de objetos do Hibernate. Lucene é uma engine de pesquisa de código aberto livremente disponível. O Apache Lucene analisa uma grande quantidade de textos e criar uma pesquisa indexada. O Lucene, por si só, é um componente que provê funcionalidades de pesquisa no seu núcleo principal, incluindo análise, indexação e consulta. Nossa aplicação se integra com o Lucene para fornecer conteúdo para indexação e consultas para executar contra a indexação.

Isso pode ser frustrante quando se trabalha com Hibernate porque há uma incompatibilidade entre Lucene e objetos Java que usam Hibernate, similar a incompatibilidade objeto/relacional. No entanto, o Hibernate Search vem para terminar com essa inconsistência.

Por trás da funcionalidade de pesquisa do Hibernate Search está o Apache Lucene, uma biblioteca de código-fonte aberto para indexação e pesquisa. O Apache Lucene é considerado um projeto Java com uma rica história de inovação, embora ainda possa ser portada para outras linguagens de programação, não se limitando apenas ao Java. O Apache Lucene é adotado em uma grande variedade de projetos na indústria, entre eles se destacam a Disney e o Twitter.

Um projeto inter-relacionado ao Apache Lucene é o Apache Solr, que é um servidor de pesquisa standalone baseado no Lucene.

No restante do artigo veremos mais sobre o Hibernate Search, seu funcionamento, as suas principais características e exemplos.

Funcionamento

O Hibernate Search é um “empacotador magro” ou um thin wrapper que abrange o Lucene e um componente opcional Solr. Ele estende o núcleo principal do Hibernate ORM, o mais utilizado framework de mapeamento objeto/relacional para Persistência Java.

A Figura 1 mostra o relacionamento entre todos esses componentes citados:

Figura 1. Relacionamento entre os componentes.

As últimas versões do Hibernate Search envolvem dois papeis principais:

O Hibernate Search esconde a maior parte do baixo nível realizado através do Lucene.

Aplicação de Exemplo

Para incorporar o Hibernate Search na nossa aplicação precisamos realizar três passos:

  1. Adicionar informação às nossas classes de entidades para que o Lucene saiba como indexa-las.
  2. Escrever uma ou mais consultas nas porções relevantes da aplicação.
  3. Configurar o projeto para que as dependências necessárias e a configuração para o Hibernate Search estejam disponíveis primeiramente.

Portanto, a primeira situação a ser feita é criar uma classe de entidade como mostrada abaixo. A classe de entidade exemplifica um login que exige um nome de usuário e senha. Observe a Listagem 1.

Listagem 1. Exemplo de uma classe de entidade.

package exemplo.hibernatesearch; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="login", schema="public") public class Login { @Id @GeneratedValue(generator="increment") @GenericGenerator(name="increment", strategy = "increment") private int id; @Column private String usuario; @Column private String senha; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsuario() { return usuario; } public void setUsuario(String usuario) { this.usuario = usuario; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } }

A classe acima diz ao Hibernate para que a classe seja mapeada para uma tabela de uma base de dados.

Agora devemos preparar essa classe de entidade para o Hibernate Search. Dessa forma, o Hibernate Search saberá como gerenciar essa classe com o Lucene.

Para isto, devemos adicionar a anotação @Indexed na classe, conforme mostrado na Listagem 2.

Listagem 2. Adicionando a anotação para o Hibernate Search.

@Entity @Indexed @Table(name="login", schema="public") public class Login { … }

Este exemplo declara que o Lucene deveria construir e usar uma indexação para este classe de entidade. Esta anotação é opcional. Quando escrevermos uma aplicação de larga escala, muitas das classes de entidades podem não ser relevantes para pesquisa. O Hibernate Search apenas necessita dizer ao Lucene sobre esses tipos que serão considerados pesquisáveis.

Após isso, devemos declarar pontos de informações que serão pesquisáveis através da anotação @Field, conforme mostra o exemplo da Listagem 3.

Listagem3.Adicionando a anotação @Field à nossa classe

package exemplo.hibernatesearch; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.search.annotations.Field; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="login", schema="public") public class Login { @Id @GeneratedValue(generator="increment") @GenericGenerator(name="increment", strategy = "increment") private int id; @Column @Field private String usuario; @Column private String senha; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsuario() { return usuario; } public void setUsuario(String usuario) { this.usuario = usuario; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } }

Podemos notar que apenas o nome de usuário obteve a anotação @Field. Isso ocorre porque não temos o interesse de pesquisar por id ou senha, por isso esses atributos não são anotados.

Para que possamos testar a nossa aplicação criaremos a classe e método que estão na Listagem 4.

Listagem 4. Construindo uma classe de teste inicial.

package exemplo.hibernatesearch; import java.util.List; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; public class TesteHibernate { //método thread-safe private static synchronized Session openSession() { Configuration conf = new Configuration(); conf.configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings (conf.getProperties()).buildServiceRegistry(); SessionFactory sessionFactory = conf.buildSessionFactory(serviceRegistry); Session session = sessionFactory.openSession(); return session; } public void teste() { } }

O método teste conterá a lógica para sessões e manipulação da base de dados. Na Listagem 5 inserimos um dado qualquer na base de dados.

Listagem 5. Inserindo dados na base de dados.

public void insere() { Session session = openSession(); try { session.beginTransaction(); Login p = new Login(); p.setSenha("user1"); p.setUsuario("senha1"); //salva usuario criado acima session.save(p); //comita a transacao session.getTransaction().commit(); } catch ( HibernateException e ) { if ( session.getTransaction() != null ) session.getTransaction().rollback(); } finally { session.close(); } }

Feito isso, podemos agora escrever nosso primeiro código de pesquisa utilizando uma simples página index.html, conforme a Listagem 6.

Listagem 6. Tela de pesquisa para nome de usuário.

<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Página de Pesquisa</title> </head> <body> <h1>Bem-vindo à Página de Exemplo</h1> Entre com um nome de usuário para pesquisa: <form action="pesquisa" method="post"> <div id="pesquisa"> <div> <input type="text" name="nomeUsuario" /> <input type="submit" value="Pesquisar" /> </div> </div> </form> </body> </html>

Também podemos fazer uma tela swing com os dados acima chamando o nosso método teste() que será definido abaixo. Não se esqueça de chamar uma vez o método insere() para adicionar alguma informação na base de dados para fins de teste.

Agora podemos executar a nossa consulta para a pesquisa. O código da Listagem 7 demonstra como poderíamos fazer isso.

Listagem 7. Criando uma sessão do Hibernate Search

import org.hibernate.Session; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; //mais códigos Session session = openSession(); FullTextSessionfullTextSession = Search.getFullTextSession(session); fullTextSession.beginTransaction();

Agora que possuímos uma sessão do Hibernate Serach podemos realizar a pesquisa com Lucene Search, conforme a Listagem 8.

Listagem 8. Criando uma pesquisa com Lucene Search.

import org.hibernate.search.query.dsl.QueryBuilder; //Mais códigos String nomeUsuario = request.getParameter("nomeUsuario"); QueryBuilder queryBuilder = fullTextSession.getSearchFactory() .buildQueryBuilder().forEntity(Login.class).get(); org.apache.lucene.search.Query luceneQuery = queryBuilder.keyword().onFields("usuario").matching (nomeUsuario).createQuery(); //Mais Códigos

QueryBuilder é utilizado para construir consultas invocando uma classe de entidade em particular. No exemplo acima construímos uma consulta do tipo keyword no campo "nomeUsuario" combinando palavras-chave em nomeUsuario.

O objeto org.apache.lucene.search.Query é traduzido pelo Hibernate Search em uma busca do Lucene. Vale salientar que isso ocorre em ambas as direções. Resultados do Lucene pode ser traduzido em um objeto org.hibernate.Query e o mesmo pode ser utilizado como qualquer consulta normal de uma base de dados. Observe a Listagem 9.

Listagem 9. Realizando uma consulta no Hibernate.

org.hibernate.Query hibernateQuery = fullTextSession.createFullTextQuery(luceneQuery, Login.class); List<App> apps = hibernateQuery.list(); request.setAttribute("apps", apps);

No exemplo acima pesquisamos todas as entidades Login que foram encontradas na nossa consulta e colocamos num servlet request.

Por fim, poderíamos exibir os dados encontrados através da JSP presente na Listagem 10.

Listagem 10. Exibindo os resultados.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ tagliburi="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <title>Pesquisa Nome de Usuário</title> </head> <body> <h1>Resultados da Pesquisa</h1> <table> <tr> <td><b>Nome de Usuário:</b></td> </tr> <c:forEachvar="app" items="$"> <tr> <td>${app.nomeUsuario}</td> </tr> </c:forEach> </table> </body> </html>

Com isso, neste artigo vimos o que é o Hibernate Search, como se dá o seu funcionamento interno, e quais são seus principais componentes. Por fim fizemos uma pequena aplicação demonstrando o seu uso.

Bibliografia

[1]Hibernate - JBoss Community, disponível em www.hibernate.org/

[2]Documentação de Referência Hibernate, disponível em https://docs.jboss.org/hibernate/core/3.6/reference/pt-BR/html/index.html

[3] Introdução ao Hibernate, disponível em https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/queryhql.html

[4] Jeff Linwood and Dave Minter. An introduction to persistence using Hibernate 3.5, Second Edition. Apress.

[5] Steve Perkins. Hibernate Search by Example: Explore the Hibernate Search system and use its extraordinary search features in your own applications. Packt Publishing.

Artigos relacionados