Introduction to Hibernate Search

Bring the power of Lucene to your database-backed applications

1 2 3 4 5 6 7 Page 4
Page 4 of 7

A Hibernate search DAO class

All the database persistence, full-text indexing, and query logic is placed in a single POJO-style DAO class: ResumeDaoHibernate.java, shown in Listing 2.

Listing 2. ResumeDaoHibernate.java

package demo.hibernatesearch.dao.hibernate;

@Repository("resumeDao")
public class ResumeDaoHibernate implements ResumeDao {

   protected final Log log = LogFactory.getLog(getClass());

   JpaTemplate jpaTemplate;

   @Autowired
   public ResumeDaoHibernate(EntityManagerFactory entityManagerFactory) {
      this.jpaTemplate = new JpaTemplate(entityManagerFactory);
   }

   //...
}

Two new annotations -- @Repository and @Autowired -- are Spring 2.5 features that suggest the DAO nature of the bean and the bean autowiring function, respectively. The DAO class is a POJO in that it doesn't inherit a JpaDaoSupport class as it might have in Spring 1- and Spring 2.0-based development, and in that the use of Spring's JpaTemplate class is not mandatory. You can inject an EntityManager instance (instead of the JpaTemplate class) via the JPA @PersistenceContext annotation.

Inside the DAO class, I first define a set of CRUD methods, as shown in Listing 3. These are the simplest CRUD methods possible in Spring-assisted JPA. There is no explicit reference to Hibernate Search or Lucene whatsoever in these methods.

Listing 3. CRUD methods

public void saveApplicant(User applicant) {
      getJpaTemplate().persist(applicant);
   }

   public void updateApplicant(User applicant) {
      getJpaTemplate().refresh(applicant);
   }

   public User getApplicant(Long id) {
      return getJpaTemplate().find(User.class, id);
   }

   public void deleteApplicant(User applicant) {
      getJpaTemplate().remove(applicant);
   }

   public void saveResume(Resume resume) {
      getJpaTemplate().persist(resume);
   }

   public void updateResume(Resume resume) {
      getJpaTemplate().refresh(resume);
   }

   public Resume getResume(Long id) {
      return getJpaTemplate().find(Resume.class, id);
   }

   public void deleteResume(Resume resume) {
      getJpaTemplate().remove(resume);
   }

Magic happens behind the scenes when Hibernate Search auto-indexing is turned on. Auto-indexing, which is enabled by default, automatically saves, updates, or deletes the Lucene Document object from index files whenever a corresponding JPA entity instance is persisted, updated, or deleted from the database through a persistence context (Hibernate Session/JPA EntityManager). This makes the entire indexing processes transparent to application developers. Manual indexing in Hibernate Search is only needed when index files are corrupted, or when there is existing data in the database to be indexed.

To differentiate and compare SQL queries with Lucene index search, JPA query methods in the DAO class are prefixed with db, while Lucene full-text search methods are prefixed with se. Listing 4 provides examples.

Listing 4. Finding resumes for a user

@SuppressWarnings("unchecked")
   public List<Resume> dbFindResumesForUser(String emailAddress) {
      return (List<Resume>) getJpaTemplate().find(
         "from Resume resume left join fetch applicant where " +
         "resume.applicant.emailAddress='" 
         + emailAddress + "'");
   }

   @SuppressWarnings("unchecked")
   public List<Resume> seFindResumesForUser(final String emailAddress) {
      Object results = getJpaTemplate().execute(new JpaCallback() {
         public Object doInJpa(EntityManager em) throws PersistenceException {

            FullTextEntityManager fullTextEntityManager = createFullTextEntityManager(em);

            TermQuery tq = new TermQuery(new Term("applicant.emailAddress", emailAddress));

            FullTextQuery fq = fullTextEntityManager.createFullTextQuery(tq, Resume.class);

            return fq.getResultList();
         }
      });
      return (List<Resume>) results;
   }

These two methods find all the resumes for an applicant with a given e-mail address. The first method is a pure JPA query expressed by JPAQL. The second is a Lucene full-text search. The FullTextEntityManager class in Hibernate Search is a decorator of the JPA EntityManager (a similar FullTextSession exists for the Hibernate Session). It executes Lucene queries declared with the Lucene query API or query expression inside a JPA persistence context. The same persistence context may be used simultaneously by JPA for database queries.

An advanced use case is to execute a full Lucene text query on an entity class, followed by a JPA query to fetch its relationships not stored in the Lucene index. The Lucene term query in this method searches the applicant.emailAddress field in the Resume index. Hibernate Search implicitly converts the returned Lucene Document objects into Resume entities as the search results. There is an implicit database query fired by Hibernate Search to fetch the resume Word documents not stored in the Lucene index as part of the returned Resume entities. It is very important to note that the returned entities are in the persistent state of the JPA entity lifecycle.

1 2 3 4 5 6 7 Page 4
Page 4 of 7