JPA Searching Using Lucene - A Working Example with Spring and DBUnit | Architects Zone




@Indexed - this identifies that the class will be added to the Lucene index. You can define a specific index by adding the 'index' attribute to the annotation. We're just choosing the simplest, minimal configuration for this example. 

In addition to this - you also need to specify which properties on the entity are to be indexed, and how they are to be indexed. For our example we are again going for the default option by just adding an @Field annotation with no extra parameters. We are adding one other annotation to the 'title' field - @Boost - this is just telling Lucene to give more weight to search term matches that appear in this field (than the same term appearing in the description field). 
@Entity
18.@Indexed
19.public class Book {
20. 
21.@Id
22.@GeneratedValue
23.private Long id;
24. 
25.@Field
26.@Boost(value = 1.5f)
27.private String title;
28. 
29.@Field
30.@Lob
31.private String description;
32. 
33.@Field
34.@Enumerated(EnumType.STRING)
35.private BookCategory category;}


public void updateFullTextIndex() throws Exception {
046.LOG.info("Updating Index");
047.getFullTextEntityManager().createIndexer().startAndWait();
048.}
049. 
050./**
051.* Add a Book to the Database.
052.*/
053.@Transactional
054.public Book addBook(Book book) {
055.LOG.info("Adding Book : " + book);
056.em.persist(book);
057.return book;
058.}
059. 
060./**
061.* Delete All Books.
062.*/
063.@SuppressWarnings("unchecked")
064.@Transactional
065.public void deleteAllBooks() {
066. 
067.LOG.info("Delete All Books");
068. 
069.Query allBooks = em.createQuery("select b from Book b");
070.List<Book> books = allBooks.getResultList();
071. 
072.// We need to delete individually (rather than a bulk delete) to ensure they are removed
073.// from the Lucene index correctly
074.for (Book b : books) {
075.em.remove(b);
076.}
077. 
078.}
079. 
080.@SuppressWarnings("unchecked")
081.@Transactional
082.public void listAllBooks() {
083. 
084.LOG.info("List All Books");
085.LOG.info("------------------------------------------");
086. 
087.Query allBooks = em.createQuery("select b from Book b");
088.List<Book> books = allBooks.getResultList();
089. 
090.for (Book b : books) {
091.LOG.info(b.toString());
092.getFullTextEntityManager().index(b);
093.}
094. 
095.}
096. 
097./**
098.* Search for a Book.
099.*/
100.@SuppressWarnings("unchecked")
101.@Transactional
102.public List<Book> search(BookCategory category, String searchString) {
103. 
104.LOG.info("------------------------------------------");
105.LOG.info("Searching Books in category '" + category + "' for phrase '" + searchString +"'");
106. 
107.// Create a Query Builder
108.QueryBuilder qb = getFullTextEntityManager().getSearchFactory().buildQueryBuilder().forEntity(Book.class).get();
109. 
110.// Create a Lucene Full Text Query
111.org.apache.lucene.search.Query luceneQuery = qb.bool()
112..must(qb.keyword().onFields("title","description").matching(searchString).createQuery())
113..must(qb.keyword().onField("category").matching(category).createQuery()).createQuery();
114. 
115.Query fullTextQuery = getFullTextEntityManager().createFullTextQuery(luceneQuery, Book.class);
116. 
117.// Run Query and print out results to console
118.List<Book> result = (List<Book>) fullTextQuery.getResultList();
119. 
120.// Log the Results
121.LOG.info("Found Matching Books :" + result.size());
122.for (Book b : result) {
123.LOG.info(" - " + b);
124.}
125. 
126.return result;
127.}
128. 
129./**
130.* Convenience method to get Full Test Entity Manager. Protected scope to assist mocking inUnit
131.* Tests.
132.* @return Full Text Entity Manager.
133.*/
134.protected FullTextEntityManager getFullTextEntityManager() {
135.if (ftem == null) {
136.ftem = Search.getFullTextEntityManager(em);
137.}
138.return ftem;
139.}

application-context.xml

This is the Spring configuration file. You can see in the JPA Entity Manager configuration the key for 'hibernate.search.default.indexBase' is added to the jpaPropertyMap to tell Lucene where to create the index. We have also externalised the database login credentials to a properties file (as you may wish to change these for different environments), for example by updating the propertyConfigurer to look for and use a different external properties if it finds one on the file system). 
<beans><context:component-scan base-package="com.cor.demo.jpa" />
14. 
15.<!-- Property configuration -->
16.<bean id="propertyConfigurer"
17.class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
18.p:ignoreUnresolvablePlaceholders="true" p:ignoreResourceNotFound="true">
19.<property name="locations">
20.<list>
21.<value>classpath:/system.properties</value>
22.</list>
23.</property>
24.</bean>
25. 
26.<!-- JPA Entity Manager Factory -->
27.<bean id="entityManagerFactory"
28.class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
29.<property name="dataSource" ref="dataSource" />
30.<!-- <property name="packagesToScan" value="com.cor.demo.jpa.entity" /> -->
31.<property name="jpaVendorAdapter">
32.<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
33.<property name="showSql" value="true" />
34.<property name="generateDdl" value="true" />
35.</bean>
36.</property>
37.<property name="jpaPropertyMap">
38.<map>
39.<entry key="hibernate.hbm2ddl.auto" value="update" />
40.<entry key="hibernate.format_sql" value="true" />
41.<entry key="hibernate.use_sql_comments" value="false" />
42.<entry key="hibernate.show_sql" value="false" />
43.<entry key="hibernate.search.default.indexBase" value="/var/lucene/indexes" />
44.</map>
45.</property>
46.</bean>
47. 
48.<!-- JPA Data Source -->
49.<bean id="dataSource"
50.class="org.springframework.jdbc.datasource.DriverManagerDataSource">
51.<property name="driverClassName" value="${database.driver}" />
52.<property name="url" value="${database.url}" />
53.<property name="username" value="${database.username}" />
54.<property name="password" value="${database.password}" />
55.</bean>
56. 
57.<!-- Transaction Manager -->
58.<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
59.<property name="entityManagerFactory" ref="entityManagerFactory" />
60.</bean>
61.<tx:annotation-driven transaction-manager="txManager" />
62. 
63.</beans>

Testing Using DBUnit

In the project is an example of using DBUnit with Spring to test adding and searching against the database using DBUnit to populate the database with test data, exercise the Book Manager search operations and then clean the database down. This is a great way to test database functionality and can be easily integrated into maven and continuous build environments.

Because DBUnit bypasses the standard JPA insertion calls - the data does not get automatically added to the Lucene index. We have a method exposed on the service interface to update the Full Text index 'updateFullTextIndex()' - calling this causes Lucene to update the index with the current data in the database. This can be useful when you are adding search to pre-populated databases to index the  existing content.

Read full article from JPA Searching Using Lucene - A Working Example with Spring and DBUnit | Architects Zone

No comments:

Post a Comment

Labels

Algorithm (219) Lucene (130) LeetCode (97) Database (36) Data Structure (33) text mining (28) Solr (27) java (27) Mathematical Algorithm (26) Difficult Algorithm (25) Logic Thinking (23) Puzzles (23) Bit Algorithms (22) Math (21) List (20) Dynamic Programming (19) Linux (19) Tree (18) Machine Learning (15) EPI (11) Queue (11) Smart Algorithm (11) Operating System (9) Java Basic (8) Recursive Algorithm (8) Stack (8) Eclipse (7) Scala (7) Tika (7) J2EE (6) Monitoring (6) Trie (6) Concurrency (5) Geometry Algorithm (5) Greedy Algorithm (5) Mahout (5) MySQL (5) xpost (5) C (4) Interview (4) Vi (4) regular expression (4) to-do (4) C++ (3) Chrome (3) Divide and Conquer (3) Graph Algorithm (3) Permutation (3) Powershell (3) Random (3) Segment Tree (3) UIMA (3) Union-Find (3) Video (3) Virtualization (3) Windows (3) XML (3) Advanced Data Structure (2) Android (2) Bash (2) Classic Algorithm (2) Debugging (2) Design Pattern (2) Google (2) Hadoop (2) Java Collections (2) Markov Chains (2) Probabilities (2) Shell (2) Site (2) Web Development (2) Workplace (2) angularjs (2) .Net (1) Amazon Interview (1) Android Studio (1) Array (1) Boilerpipe (1) Book Notes (1) ChromeOS (1) Chromebook (1) Codility (1) Desgin (1) Design (1) Divide and Conqure (1) GAE (1) Google Interview (1) Great Stuff (1) Hash (1) High Tech Companies (1) Improving (1) LifeTips (1) Maven (1) Network (1) Performance (1) Programming (1) Resources (1) Sampling (1) Sed (1) Smart Thinking (1) Sort (1) Spark (1) Stanford NLP (1) System Design (1) Trove (1) VIP (1) tools (1)

Popular Posts