Full Text Hibernate Lucene Search Hello World Example Using Maven and SQLite
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 3 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'.
1
|
mvn archetype:generate -DgroupId=com.srccodes.example.hibernate -DartifactId=HibernateSearchHelloWorld -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=
false
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
<
project
xmlns
=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
<
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
>
<
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.
1
|
mvn eclipse:eclipse
|
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.
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
<
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
>
|
'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.
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
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();
}
}
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
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();
}
}
|
'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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package
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);
}
}
}
}
|
'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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
Id: 1 | Name:Abhijit Ghosh | Email:abhijit@email.com
Id: 3 | Name:My Name | Email:my_email@email.com
Id: 5 | Name:Li Chao | Email:lichao@email.com
Id: 6 | Name:Tom Li | Email:tomli@email.com
Id: 24 | Name:Your Name | Email:your_email@email.com
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:my_email@email.com
Id: 24 | Name:Your Name | Email:your_email@email.com
Enter search key (To
exit
type
'X'
)Li
>>>>>>Record found
for
'Li'
Id: 5 | Name:Li Chao | Email:lichao@email.com
Id: 6 | Name:Tom Li | Email:tomli@email.com
Enter search key (To
exit
type
'X'
)
test
>>>>>>Record found
for
'test'
Enter search key (To
exit
type
'X'
)x
End
|
Download Source Code