Hibernate Search is a full text search engine built using Apache Lucene framework. Hibernate Search indexes your domain model, keep the index upto date and also performs full text search functionality to fetch matching domain objects. This Hello World example shows how you can setup hibernate search and get your domain objects based on free text search query. In this tutorial we will use Maven tool to build the project, Eclipse IDE to code and SQLite database to save our domain objects.
To make the learning easier, I have used SQLite database which is a self-contained, serverless, zero-configuration, transactional SQL database engine. But you are free to choose any other database as well to learn this tutorial. To run example shown in this tutorial, you do not have to install any database or SQLite database seperately. However to browse the database you can use 'SQLite Manager - Firefox addon' which provides a very nice GUI for SQLite database.
Tools and Technologies used in this article
- Hibernate Search 4.1
- Maven
- SQLite database
- SQLite Manager - Firefox addon
- Eclipse 3.7
1. Create a Java Project using Maven Tool
In the command prompt execute the following command to generate Maven compatible Java project named as 'HibernateSearchHelloWorld'.
mvn archetype:generate -DgroupId=com.srccodes.example.hibernate -DartifactId=HibernateSearchHelloWorld -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Generated Maven Java Project structure
2. Update pom.xml
Add dependency of Hibernate core and SQLite jdbc library. Also update 'maven-compiler-plugin' so that it uses compilation level 1.5 onwards. Otherwise annotation (introduced in JDK 5) will not work.
File : pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.srccodes.example.hibernate</groupId>
<artifactId>HibernateSearchHelloWorld</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>HibernateSearchHelloWorld</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.4.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<version>4.1.1.Final</version>
</dependency>
<!-- SQLite JDBC library -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
</dependency>
<!-- junit test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3. Convert to Eclipse compatible Java project
Open the directory 'HibernateSearchHelloWorld' in command prompt and run the following maven command.
mvn eclipse:eclipse
Screenshot of command prompt
On completion of the above command, Maven Java project will be converted to a Eclipse compatible java project.
Eclipse compatible Java Project structure
4. Import project in Eclipse
Open Eclipse IDE and select from the menu File --> Import --> General --> Existing Projects into Workspace
Browse to the directory of the newly converted Eclipse compatible Java Project and click 'Finish' button.
Screenshot of Eclipse project structure
5. Add Hibernate Configuration file
Right click on 'main' and select from context menu 'New' --> 'Folder'.
Enter 'resources' in the 'Folder name' field and click the 'Finish' button.
Copy the 'hibernate.cfg.xml' file in the 'resources' folder.
File: hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="show_sql">false</property>
<property name="format_sql">true</property>
<property name="dialect">org.hibernate.dialect.SQLiteDialect</property>
<property name="connection.driver_class">org.sqlite.JDBC</property>
<property name="connection.url">jdbc:sqlite:mydb.db</property>
<property name="connection.username"></property>
<property name="connection.password"></property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.search.default.directory_provider">filesystem</property>
<property name="hibernate.search.default.indexBase">C:\lucene\indexes</property>
<mapping class="com.srccodes.example.hibernate.Contact"/>
</session-factory>
</hibernate-configuration>
Note :
'mydb.db' is the SQLite database file included with the sourcecode attached in 'Download Source Code' section. You can create db file of your own using 'SQLite Manager - Firefox addon' UI. But copy that file inside 'HibernateSearchHelloWorld' project directory directly.
I have set the property 'hibernate.hbm2ddl.auto' to 'update' so that when you will execute the code it will create the database tables of it's own based on the entity class 'com.srccodes.example.hibernate.Contact') we have written and referenced in this configuration file.
Set property 'hibernate.search.default.indexBase' to a writeable directory where Lucene will create index for your domain objects.
6. Configure Java Build Path
Right click on 'HibernateSearchHelloWorld' project and select from context menu 'Properties' --> 'Java Build Path'.
Add 'resources' folder as shown in the screenshot below
7. Add SQLiteDialect
Copy from attached source code or download SQLiteDialect and add under package 'org.hibernate.dialect' in your project.
Note: dialect is used to help hibernate framework to create underlying database specific SQL query.
8. Write Entity class
Create a class 'Contact' under the package 'com.srccodes.example.hibernate' and copy the following content.
File: Contact.java
package com.srccodes.example.hibernate;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;
/**
* The persistent class for the contact database table.
*
*/
@Entity
@Indexed
@Table(name = "contact")
public class Contact {
private Integer id;
private String name;
private String email;
public Contact() {
}
public Contact(Integer id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
@Id
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("Id: ").append(this.getId()).append(" | Name:").append(this.getName()).append(" | Email:").append(this.getEmail());
return stringBuilder.toString();
}
}
Note :
To know basic hibernate annotation follow the tutorial Hibernate Hello World example using Maven build tool and SQLite database
@Indexed annotation specifies an entity as indexable.
@Field annotation specifies an field as searchable. Here Index.YES means 'name' field will indexed, Analyze.YES means that filed will be analyzed (excludes common stop words like 'a', 'an', 'and', 'the' etc) using default Lucene Analyzer, Store.NO means actual 'name' field data will not be stored in the index.
I have also written 'toString()' method which we'll use to print out domain objects in console.
9. Common utility class for Hibernate
Copy the following code to 'HibernateUtil' class of package 'com.srccodes.example.hibernate'.
File: HibernateUtil.java
package com.srccodes.example.hibernate;
import java.util.Properties;
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;
/**
* Contains utility methods
*
* @author srccodes.com
* @version 1.0
*
*/
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
private static ServiceRegistry serviceRegistry = null;
private static SessionFactory configureSessionFactory() throws HibernateException {
Configuration configuration = new Configuration();
configuration.configure();
Properties properties = configuration.getProperties();
serviceRegistry = new ServiceRegistryBuilder().applySettings(properties).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
// We need to configure session factory once.
// Rest of the time we will get session using the same.
static {
configureSessionFactory();
}
private HibernateUtil() {}
public static Session getSession() {
return sessionFactory.openSession();
}
}
Note:
'configureSessionFactory()' method will build the hibernate session factory based on the configuration in 'hibernet.cfg.xml' file.
'getSession()' method will provide a Hibernate session from the configured session factory.
10. Hibernate Search and Indexing code
Copy the following code to 'App' class of package 'com.srccodes.example.hibernate'.
File: App.java
ackage com.srccodes.example.hibernate;
import java.util.List;
import java.util.Scanner;
import org.hibernate.Session;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.query.dsl.QueryBuilder;
/**
* Hello world!
*
*/
public class App {
private static void doIndex() throws InterruptedException {
Session session = HibernateUtil.getSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.createIndexer().startAndWait();
fullTextSession.close();
}
private static List<Contact> search(String queryString) {
Session session = HibernateUtil.getSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Contact.class).get();
org.apache.lucene.search.Query luceneQuery = queryBuilder.keyword().onFields("name").matching(queryString).createQuery();
// wrap Lucene query in a javax.persistence.Query
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, Contact.class);
List<Contact> contactList = fullTextQuery.list();
fullTextSession.close();
return contactList;
}
private static void displayContactTableData() {
Session session = null;
try {
session = HibernateUtil.getSession();
// Fetching saved data
List<Contact> contactList = session.createQuery("from Contact").list();
for (Contact contact : contactList) {
System.out.println(contact);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally{
if(session != null) {
session.close();
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("\n\n******Data stored in Contact table******\n");
displayContactTableData();
// Create an initial Lucene index for the data already present in the database
doIndex();
Scanner scanner = new Scanner(System.in);
String consoleInput = null;
while (true) {
// Prompt the user to enter query string
System.out.print("\n\nEnter search key (To exit type 'X')");
consoleInput = scanner.nextLine();
if("X".equalsIgnoreCase(consoleInput)) {
System.out.println("End");
System.exit(0);
}
List<Contact> result = search(consoleInput);
System.out.println("\n\n>>>>>>Record found for '" + consoleInput + "'");
for (Contact contact : result) {
System.out.println(contact);
}
}
}
}
Note :
'doIndex()' method will generate indexes for your domain objects in the directory specified in the 'hibernate.search.default.indexBase' property in 'hibernate.cfg.xml' file.
'search(String queryString)' method will prepare a Lucene search query based on the query string. Then that query will be wrapped in a javax.persistence.Query which will be executed to return matching domain objects.
11. Final project structure
After doing all the changes the overall project structure will look like this
12. Populate 'Contact' table with data
Browse the 'mydb.db' SQLite database file (under 'HibernateSearchHelloWorld' project) using 'SQLite Manager - Firefox addon'. Populate the 'Contact' table with some dummy test data.
Screenshot of the table using 'SQLite Manager - Firefox addon' UI
13. Run Your Code
Right click on 'App.java' and select from context menu 'Run As' --> 'Java Application'.
13. Console Output
In the console, all the records of the 'Contact' table will be printed first. Then you will be prompted to enter search key. Based on the search key Hibernate Search API will return all the matching domain objects which will be printed in the console. To exit from the command prompt you have to type 'X'.
Console
Id: 1 | Name:Abhijit Ghosh | Email:[email protected]
Id: 3 | Name:My Name | Email:[email protected]
Id: 5 | Name:Li Chao | Email:[email protected]
Id: 6 | Name:Tom Li | Email:[email protected]
Id: 24 | Name:Your Name | Email:[email protected]
Jul 26, 2012 5:41:32 PM org.hibernate.search.impl.SimpleIndexingProgressMonitor addToTotalCount
INFO: HSEARCH000027: Going to reindex 5 entities
Jul 26, 2012 5:41:32 PM org.hibernate.search.impl.SimpleIndexingProgressMonitor indexingCompleted
INFO: HSEARCH000028: Reindexed 5 entities
Enter search key (To exit type 'X')name
>>>>>>Record found for 'name'
Id: 3 | Name:My Name | Email:[email protected]
Id: 24 | Name:Your Name | Email:[email protected]
Enter search key (To exit type 'X')Li
>>>>>>Record found for 'Li'
Id: 5 | Name:Li Chao | Email:[email protected]
Id: 6 | Name:Tom Li | Email:[email protected]
Enter search key (To exit type 'X')test
>>>>>>Record found for 'test'
Enter search key (To exit type 'X')x
End
Download SrcCodes
All code samples shown in this post are available in the following link HibernateSearchHelloWorld.zip
Comments