Spring Boot + JPA + Hibernate를 사용해 서버를 구성하였다.
search 기능이 필요해 Hibernate Search를 넣었는데 사용법은 아래와 같다.
Step)
1. pom.xml에 hibernate-search-orm 추가.
2. Entity 추가.
3. ②번의 entity searching을 위한 custom repository interface 추가.
4. ②번의 JpaRepository를 만들고 ④번 repository extends.
5. ③번의 repository searching interface implement.
6. ⑤에서 사용할 NameFilterFactory 추가.
1. pom.xml에 hibernate search 추가
1
2
3
4
5
6
7
| <!-- Hibernate Search -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search-orm</artifactId>
<version>5.11.1.Final</version>
</dependency>
|
2. MyEntity Entity 생성
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
| @Indexed
@Entity
@Table(name = "my_entity")
@Getter
@AnalyzerDef(
name = "emailAnalyzer",
tokenizer = @TokenizerDef(factory = ClassicTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class)
}
)
@FullTextFilterDef(name = "NameFilterFactory", impl = NameFilterFactory.class)
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "card_id")
@SortableField
private Long id;
@Column(name = "name")
@Setter
@Field
@SortableField
private String name;
@Column(name = "email")
@Setter
@Field
@Analyzer(definition = "emailAnalyzer")
@SortableField
private String email;
@Column(name = "phone")
@Setter
@Field
@SortableField
private String phone;
public MyEntity(String name,
String email,
String phone) {
this.name = name;
this.email = email;
this.phone = phone;
}
}
|
설명)
- @Indexed로 searching 할 entity 추가.
- @Field로 searching 대상이 될 field 추가.
- @SortableField로 sort 대상이 될 field 추가.
- AnalyzerDef로 email searching을 위한 analyzer 추가.
- FullTextFilterDef로 searching 결과 filtering 할 filter factory 추가.
3. Searching을 위한 Custom Repository 생성
1
2
3
| public interface MyEntitySearchRepository {
List<MyEntity> searchKeyword(String name, String keyword, Pageable pageable);
}
|
설명)
- name은 filtering할 대상.
- keyword는 searching keyword.
- pageable은 spring에서 paging에 사용하는 Pageable 객체.
4. JpaRepository 생성
1
2
| public interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyEntitySearchRepository {
}
|
설명)
- JpaRepository 생성하면서 searching을 위한 interface extends.
- Custom repository로 만든 이유는 @PersistenceContext로 EntityManager 가져오기 위해.
5. Searching Interface Implement
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
| @Component(value = "myEntitySearchRepository")
public class MyEntitySearchRepositoryImpl implements MyEntitySearchRepository {
@PersistenceContext
private EntityManager entityManager;
public void initializeHibernateSearch() {
try {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
fullTextEntityManager.createIndexer().startAndWait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public List<MyEntity> searchKeyword(String name, String keyword, Pageable pageable) {
// create lucene search index.
initializeHibernateSearch();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qBuilder = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder()
.forEntity(MyEntity.class)
.get();
// Lucene query by Hibernate DSL
Query luceneQuery = qBuilder
.keyword()
.wildcard()
.onFields("email", "phone")
.matching(keyword)
.createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, MyEntity.class);
// Search filter by name
fullTextQuery.enableFullTextFilter("NameFilterFactory").setParameter("name", name);
// pagination
// convert to start element
int page = pageable.getPageNumber() * pageable.getPageSize();
int size = pageable.getPageSize();
fullTextQuery.setFirstResult(page);
fullTextQuery.setMaxResults(size);
// Sort
if (!pageable.getSort().isUnsorted()) {
org.apache.lucene.search.Sort sort = new org.apache.lucene.search.Sort();
Iterator<Sort.Order> orders = pageable.getSort().iterator();
Sort.Order o = orders.next();
if (o.getDirection().equals(Sort.Direction.ASC)) {
sort.setSort(new SortField(o.getProperty(), SortField.Type.STRING, false));
} else {
sort.setSort(new SortField(o.getProperty(), SortField.Type.STRING, true));
}
fullTextQuery.setSort(sort);
}
// If want to get total count.
// int total = fullTextQuery.getResultSize();
return fullTextQuery.getResultList();
}
}
|
설명)
- initializeHibernateSearch()는 Lucene indexing을 위한 함수.
- hibernate의 CRUD는 자동 indexing 되기 때문에 기존의 data가 있는 경우에 실행하면 됨.
- 위 코드는 searching 할때마다 indexing이 호출 되는데 그래도 되나?
- keyword searching하고 결과를 name으로 filtering.
- paging과 sorting도 처리 되어 있음.
6. NameFilterFactory 생성
1
2
3
4
5
6
7
8
9
10
11
12
| public class NameFilterFactory {
private String name;
public void setName(String name) {
this.name = name;
}
@Factory
public Query getFilter() {
return new TermQuery(new Term("name", name));
}
}
|
설명)
- searching에서 filtering할 name field factory.
댓글
댓글 쓰기