2018-12-28 · Develop

Spring Boot 集成 Elasticsearch

前面一篇文章Elasticsearch 入门教程介绍了 ES 的一些概念和一些简单的用法,下面我们将 ES 集成进 Spring Boot 应用中。

ES 是 RESTFul 接口,因此直接构造 REST 请求和解析请求的响应就可以访问 ES 服务器,而 Spring Boot 提供了 RestTemplate 来简化对 HTTP 的操作。

比如我们创建一个实体

@Builder
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Long departmentId;
    private Date createTime;
}

然后使用 restTemplate 来添加一条文档和通过主键查询添加的文档

@RunWith(SpringRunner.class)
@SpringBootTest
// @Transactional
public class ClientTest {

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    private RestTemplate restTemplate;

    @Before
    public void setUp() throws Exception {
        restTemplate = restTemplateBuilder
                .rootUri("http://192.168.113.131:9200")
                .build();
    }

    @Test
    public void save() throws Exception {
        User user = User.builder()
                .id(1L)
                .name("TEST")
                .departmentId(1L)
                .createTime(new Date())
                .build();

        ResponseEntity<String> entity = restTemplate.postForEntity("/lo/user/{id}?pretty", user, String.class, 1);
        Assert.assertTrue(JsonPath.parse(entity.getBody()).read("$._shards.successful", Integer.class) > 0);
    }

    @Test
    public void selectById() throws Exception {
        ResponseEntity<String> entity = restTemplate.getForEntity("/lo/user/{id}?pretty", String.class, 1);
        Assert.assertEquals("TEST", JsonPath.read(entity.getBody(), "$._source.name"));
    }
}

Spring Data Elastic

除了可以使用 RestTemplate 外, Spring 还为我们封装了 Spring Data Elastic 来简化开发和维护,但同时也舍弃了部分复杂的查询和统计分析功能。还有就是 Spring Data Elastic 的迭代适度并不同跟上 ES 的迭代,导致部分新功能无法支持的情况。集成到还是一贯的简单

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

添加配置信息

spring:
  data:
    elasticsearch:
      cluster-nodes: 192.168.113.131:9200

编写实体类

@Builder
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "lo", type = "user")
public class User implements Serializable {
    @Id
    private Long id;
    private String name;
    private Long departmentId;
    private Date createTime;
}

@Document 注解表明这是一个 Elastic Data 其中的 indexName 和 type 对应于 Elasticsearch 中的 Index 和 Type。
@Id 声明了文档的主键使用的是 Spring Data 的注解。

编写 dao 层代码

public interface UserDao extends CrudRepository<User, Long> {

    List<User> getByName(String name);

    Page<User> getByName(String name, Pageable pageable);
}

通过集成 Spring Data 的 CrudRepository 来简化增删改查操作,也可以添加符合 Spring Data 命名的方法。

下面看看测试代码

@RunWith(SpringRunner.class)
@SpringBootTest
//@Transactional
public class UserDaoTest {

    @Autowired
    private UserDao userDao;

    static {
        System.setProperty("es.set.netty.runtime.available.processors", "false");
    }

    @Test
    public void save() throws Exception {
        userDao.save(User.builder()
                .id(1L)
                .name("TEST")
                .departmentId(1L)
                .createTime(new Date())
                .build());
    }

    @Test
    public void selectById() throws Exception {
        Optional<User> bookEntityOptional = userDao.findById(1L);
        User user = bookEntityOptional.get();
        assertTrue(1L == user.getId());
    }

    @Test
    public void selectPage() throws Exception {
        Page<User> page = userDao.getByName("TEST", PageRequest.of(0, 10));
        assertTrue(page.getContent().size() > 0);
    }
}

其中设置变量 es.set.netty.runtime.available.processors 的值为 false 是因为,如果不设置会有如下的错误信息

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchClient' defined in class path resource [org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'elasticsearchClient' threw exception; nested exception is java.lang.IllegalStateException: availableProcessors is already set to [8], rejecting [8]

可以参考这个 Issues