spring data elasticsearch 사용해 보기

프로그래밍|2017. 8. 31. 12:15

실무에서 elasticsearch 와 연동해야 할 일이 생겼다.

검색을 통해서 알아보니 Spring Data 프로젝트 하위에 Spring Data Elasticsearch 프로젝트가 존재했다.

https://github.com/spring-projects/spring-data-elasticsearch

그 외에 Elasticsearch와 연동 가능한 Java API가 있었지만 Spring Boot를 사용하고 있어 Spring Data를 선택하게 되었다.

선택 이유는 Spring Boot + Spring Data Elasticsearch 통합이 쉽기 때문이다.

      

일단 ElasticSearch와 연동하기 위해선 설치 작업이 선행되어야 한다.

개발 환경이 윈도우라서 난 윈도우 버전을 설치했다.

다른 환경이라고 해서 딱히 설치 과정이 복잡하지 않다. 그저 바이너리 다운로드 받고 압축을 푼 다음 실행만 하면 된다.

설치 방법 : http://lng1982.tistory.com/283


설치가 완료되고 Elasticsearch 서버가 구동되었다면 Spring Data Elasticsearch 셋팅을 해보자.


pom.xml 파일에 dependency 추가해 준다.

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
cs


모델을 생성하자.

indexName에 추가되어 있는 nklee는 RDB관점에서 봤을 때 데이터베이스와 유사하다.

type은 테이블과 유사하다.

1
2
3
4
5
6
7
8
9
10
11
12
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@lombok.ToString
@Document(indexName = "nklee", type = "phone")
public class Phone {
    @Id
    private int id;
    private String number;
    private String author;
}
cs


Repository를 생성하자.

이 구조 낯이 익다.

Spring Data JPA 도 이와같이 Repository 인터페이스를 상속하는데 Spring Data Elasticsearch 또한 유사하다.

아마도 Spring Data 프로젝트의 모든 서브 프로젝트들이 이와 같은 concept으로 되어 있지 않을까?

1
2
3
4
public interface PhoneRepository extends ElasticsearchCrudRepository<Phone, Integer> {
    Page<Phone> findByAuthor(String author, Pageable pageable);
    List<Phone> findByNumber(String number);
}
cs


Service를 생성하자.

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
@Service
public class PhoneService {
 
    @Autowired
    private PhoneRepository phoneRepository;
 
    public Phone save(Phone phone) {
        return phoneRepository.save(phone);
    }
 
    public void delete(Phone phone) {
        phoneRepository.delete(phone);
    }
 
    public Phone findOne(Integer id) {
        return phoneRepository.findOne(id);
    }
 
    public Iterable<Phone> findAll() {
        return phoneRepository.findAll();
    }
 
    public Page<Phone> findByAuthor(String author, PageRequest pageRequest) {
        return phoneRepository.findByAuthor(author, pageRequest);
    }
 
    public List<Phone> findByNumber(String number) {
        return phoneRepository.findByNumber(number);
    }
}
cs


이제 Elasticsearch 서버와 연동하기 위한 ElasticsearchTemplate 설정을 해야 한다.

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
@Configuration
@EnableElasticsearchRepositories
class EsConfig {
 
    @Value("${elasticsearch.host}")
    private String EsHost;
 
    @Value("${elasticsearch.port}")
    private int EsPort;
 
    @Value("${elasticsearch.clustername}")
    private String EsClusterName;
 
    @Bean
    public Client client() throws Exception {
        Settings esSettings = Settings.settingsBuilder()
                .put("cluster.name", EsClusterName)
                .build();
        
        return TransportClient.builder()
                .settings(esSettings)
                .build()
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(EsHost), EsPort));
    }
 
    @Bean
    public ElasticsearchOperations elasticsearchTemplate() throws Exception {
        return new ElasticsearchTemplate(client());
    }
}
cs


application.properties

1
2
3
4
elasticsearch.clustername=my-application
elasticsearch.host=localhost
elasticsearch.port=9300
logging.config=classpath:logback-spring.xml
cs

나는 클러스터 이름을 my-application 이라고 했다.

Elasticsearch 서버의 config/elasticsearch.yml 파일에서 클러스터 이름을 지정할 수 있다.



이제 준비가 다 되었으니 테스트를 해보자.

Spring Data Elasticsearch를 사용하게 됨으로써 코드의 간결함을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void 데이터저장() {
    Phone phone = new Phone(1"010-1111-1111""nklee");
    Phone testPhone = phoneService.save(phone);
 
    assertNotNull(testPhone.getId());
    assertThat(testPhone.getNumber(), is(phone.getNumber()));
}
 
@Test
public void 조회() {
    Phone phone = new Phone(1"010-1111-1111""nklee");
    phoneService.save(phone);
 
    Phone testPhone = phoneService.findOne(1);
    assertNotNull(testPhone.getId());
    assertThat(testPhone.getNumber(), is(phone.getNumber()));
}
cs


전체 소스 코드는 Github에 올려놨다.

https://github.com/namkyu/test_spring_data


추가적으로 Spring Data Elasticsearch를 사용하기 위해서는 버전에 대해서 염두해야 한다.

Spring Data Elasticsearch 버전에 호환되는 Elasticsearch 서버가 존재한다는 의미이다.


내가 겪었던 문제에 대해서 적어본다.

Spring Data Elasticsearch를 이용해 Elasticsearch 서버와 연동을 하려고 하는데 아래와 같은 오류 메세지를 만났다.

1
org.elasticsearch.transport.ReceiveTimeoutTransportException: [][127.0.0.1:9200][cluster:monitor/nodes/liveness] request_id [0] timed out after [5001ms]
cs

확인해 보니 버전 오류로 발생되는 것이였다.

elasticsearch-5.5.2 버전을 구동하고 spring-data-elasticsearch 버전을 2.1.6.RELEASE 를 이용했기 때문이다.

현재 spring-data-elasticsearch 최신 2.1.6.RELEASE 버전은 Elasticsearch 5.x 버전을 지원하고 있지 않다. (2017-08-31)


[추가]

5.x 지원 가능한 spring-data-elasticsearch RC 버전이 최근에서야 추가된 것을 확인했다.



참고


지원 버전

https://github.com/spring-projects/spring-data-elasticsearch/wiki/Spring-Data-Elasticsearch---Spring-Boot---version-matrix


버전 관련 질문

https://stackoverflow.com/questions/42001828/does-spring-data-support-elasticsearch-5-x


Spring Data Elasticsearch 프로젝트

https://github.com/wonwoo/spring-boot-elasticsearch


Elasticsearch 기본 설명

http://d2.naver.com/helloworld/273788

https://iju707.gitbooks.io/elasticsearch/content/_introducing_the_query_language.html

https://github.com/spring-projects/spring-data-elasticsearch


예제 코드

https://examples.javacodegeeks.com/enterprise-java/spring/spring-data-elasticsearch-example/

https://www.mkyong.com/spring-boot/spring-boot-spring-data-elasticsearch-example/

http://www.mkyong.com/elasticsearch/elasticsearch-hello-world-example/

'프로그래밍' 카테고리의 다른 글

14. [JPA] Lock - 잠금  (0) 2017.09.20
13. [JPA] 트랜잭션 격리 수준 (Transaction Isolation Level)  (0) 2017.09.12
12. [JPA] 복합키  (0) 2017.09.08
11. [JPA] Querydsl  (0) 2017.09.01
10. [JPA] JPQL  (0) 2017.08.28
9. [JPA] @Enumerated  (0) 2017.08.23
8. [JPA] Attribute Converter  (0) 2017.08.23
7. [JPA] fetch type - 로딩 기법  (0) 2017.08.21

댓글()