spring-boot-starter-data-elasticsearch 2.5.7 的增删改查用法

1.针对 spring-boot-starter-elasticsearch的一些新特性进行了一个demo开发

1.2 主要包括路由@Routing和生命周期的回调,以及审计的用法,以及Elasticseach本身的乐观锁的实验,废话不多说,直接上代码

1.3 钩子方法

package com.example.demo.callback;

import com.example.demo.entity.TestESEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.event.AfterConvertCallback;
import org.springframework.data.elasticsearch.core.event.AfterSaveCallback;
import org.springframework.data.elasticsearch.core.event.BeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.stereotype.Component;

/**
 * 钩子方法类根据order的顺序执行
 *
 * @author dengmeiluan
 * @date 2021/12/1 14:26
 * @return
 */
@Order(1)
@Component
@Slf4j
public class TestCallBack implements
        BeforeConvertCallback<TestESEntity>,
        AfterConvertCallback<TestESEntity>,
        AfterSaveCallback<TestESEntity> {

    /**
     * 在持久化保存之前调用
     *
     * @param entity 保存的实体
     * @param index  索引
     * @return com.example.demo.entity.TestESEntity
     * @author dengmeiluan
     * @date 2021/12/1 14:26
     */
    @Override
    public TestESEntity onBeforeConvert(TestESEntity entity, IndexCoordinates index) {
        log.info("onBeforeConvert:{}", entity);
        return entity;
    }

    /**
     * 查询返回转换之前会调用
     *
     * @param entity           实体
     * @param document         文档
     * @param indexCoordinates 索引
     * @return com.example.demo.entity.TestESEntity
     * @author dengmeiluan
     * @date 2021/12/1 14:27
     */
    @Override
    public TestESEntity onAfterConvert(TestESEntity entity, Document document, IndexCoordinates indexCoordinates) {
        log.info("onAfterConvert:entity{},document{}", entity, document);
        return entity;
    }

    /**
     * 持久化保存后会调用
     *
     * @param entity 实体
     * @param index  索引
     * @return com.example.demo.entity.TestESEntity
     * @author dengmeiluan
     * @date 2021/12/1 14:27
     */
    @Override
    public TestESEntity onAfterSave(TestESEntity entity, IndexCoordinates index) {
        log.info("onAfterSave:entity{}", entity);
        return entity;
    }
}

1.4 审计获取用户信息

package com.example.demo.config;

import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;

import java.util.Optional;

/**
 * 该aware主要用于获取用户信息
 *
 * @author dengmeiluan
 * @date 2021/12/1 14:25
 * @return
 */
@Component  // 交由spring管理,否则无法获取用户信息
public class TestESAuditorAware implements AuditorAware<String> {

    /**
     * 获取用户信息的方法
     *
     * @return java.util.Optional
     * @author dengmeiluan
     * @date 2021/12/1 14:26
     */
    @Override
    public Optional<String> getCurrentAuditor() {
        return Optional.of("zhang-san");
    }
}

1.5DAO层方法

package com.example.demo.dao;

import com.example.demo.entity.TestESEntity;
import com.example.demo.mapper.TestMapper;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.*;

@Repository
public class TestESDAO {

    @Resource
    private ElasticsearchOperations elasticsearchOperations;
    @Resource
    private TestMapper testMapper;

    /**
     * 保存
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:24
     */
    public void save() {
        String[] name = {"xinLang", "tengXun", "aLiBaBa", "meiTuan", "gaoDe", "diDi", "pingGuo"};
        String[] nameChina = {"新浪公司", "腾讯集团", "阿里巴巴集团", "美团公司", "高德公司", "滴滴公司", "苹果集团"};
        List<TestESEntity> testESEntityArrayList = new ArrayList<>();
        for (int i = 1; i < 15; i++) {
            TestESEntity testEsEntity = new TestESEntity();
            testEsEntity.setId((long) i);
            int index = i % 7;
            testEsEntity.setName(name[index]);
            testEsEntity.setNameChina(nameChina[index]);
            testEsEntity.setUpdateField(7777777);
            testEsEntity.setFirst(true);//第一次
            testESEntityArrayList.add(testEsEntity);
        }
        elasticsearchOperations.save(testESEntityArrayList);
    }

    /**
     * 先查后保存
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:24
     */
    public void updateSave() {
        TestESEntity testESEntity = testMapper.queryTestESEntityById(1L);
        testESEntity.setUpdateField(9999);
        elasticsearchOperations.save(testESEntity);
    }

    /**
     * 路由搜索+分页+高亮
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:24
     */
    public void search() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withRoute("tengXun");
        //查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery("腾讯", "nameChina"));
        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("nameChina");
        highlightBuilder.highlightQuery(boolQueryBuilder);
        //组装
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        nativeSearchQueryBuilder.withHighlightBuilder(highlightBuilder);
        //分页
        nativeSearchQueryBuilder.withPageable(PageRequest.of(0, 5));
        NativeSearchQuery build = nativeSearchQueryBuilder.build();
        SearchHits<TestESEntity> searchHits = elasticsearchOperations.search(build, TestESEntity.class);
        long totalHits = searchHits.getTotalHits();
        float maxScore = searchHits.getMaxScore();
        System.out.println("totalHits:" + totalHits + ";maxScore:" + maxScore);
        for (SearchHit<TestESEntity> searchHit : searchHits) {
            //获取内容
            TestESEntity testESEntity = searchHit.getContent();
            System.out.println(testESEntity);
            //获取高亮
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            System.out.println(highlightFields);
            //获取分数
            float score = searchHit.getScore();
            System.out.println(score);
        }
    }

    /**
     * 删除
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:24
     */
    public void deleteByQuery() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.boolQuery().must(QueryBuilders.multiMatchQuery("公司", "nameChina")));
        SearchHits<TestESEntity> search = elasticsearchOperations.search(nativeSearchQueryBuilder.build(), TestESEntity.class);
        for (SearchHit<TestESEntity> sevenVersionTestESSearchHit : search) {
            System.out.println(sevenVersionTestESSearchHit.getContent());
        }
        ByQueryResponse delete = elasticsearchOperations.delete(nativeSearchQueryBuilder.build(), TestESEntity.class);
        System.out.println(delete.getTotal());
        search = elasticsearchOperations.search(nativeSearchQueryBuilder.build(), TestESEntity.class);
        for (SearchHit<TestESEntity> sevenVersionTestESSearchHit : search) {
            System.out.println(sevenVersionTestESSearchHit.getContent());
        }
    }

    /**
     * 批量更新,需要使用到script脚本
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:24
     */
    public void updateByQuery() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withRoute("tengXun");
        //查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery("腾讯", "nameChina"));
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        UpdateQuery.Builder builder = UpdateQuery.builder(nativeSearchQueryBuilder.build());
        //翻看源码,updateQuery只指出script脚本,通过文档传输进去的是无用的
        HashMap<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("up", "up");
        //构建脚本
        builder.withScriptType(org.springframework.data.elasticsearch.core.ScriptType.INLINE);
        builder.withLang(Script.DEFAULT_SCRIPT_LANG);
        builder.withParams(paramsMap);
        //如果能封装成传实体进行简单更新就好了
        builder.withScript("ctx._source.up = params.up");
        builder.withRetryOnConflict(5);
        builder.withRouting("tengXun");
        ByQueryResponse byQueryResponse = elasticsearchOperations.updateByQuery(builder.build(), elasticsearchOperations.getIndexCoordinatesFor(TestESEntity.class));
        long total = byQueryResponse.getTotal();
        System.out.println(total);
    }

    /**
     * 根据id进行更新
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:25
     */
    public void updateById() {
        List<UpdateQuery> testClasses = new ArrayList<>();
        IndexCoordinates indexCoordinates = elasticsearchOperations.getIndexCoordinatesFor(TestESEntity.class);
        for (int i = 0; i < 75; i++) {
            UpdateQuery.Builder builder = UpdateQuery.builder("1");
            builder.withRouting("tengXun");
            LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
            linkedHashMap.put("updateField", i);
            Document document = Document.from(linkedHashMap);
            builder.withDocument(document);
            //region
            // 两个搭配使用,可以在不算太高的并发场景下成功更新
            builder.withRetryOnConflict(100);//重试次数
            builder.withRefreshPolicy(RefreshPolicy.IMMEDIATE);
            // region
            testClasses.add(builder.build());
        }
        testClasses.stream().parallel().forEach(x -> {
            elasticsearchOperations.update(x, indexCoordinates);
        });
    }

    /**
     * 统计
     *
     * @return void
     * @author dengmeiluan
     * @date 2021/12/1 14:25
     */
    public void count() {
        System.out.println(testMapper.count());
    }
}

1.6 实体定义

package com.example.demo.entity;

import org.springframework.data.annotation.*;
import org.springframework.data.domain.Persistable;
import org.springframework.data.elasticsearch.annotations.*;

/**
 * ES实体,routing可以指定bean的方法也可以直接写属性名,如果要使用到审计自动赋值用户和更新时间的话,需要实现Persistable
 *
 * @author dengmeiluan
 * @date 2021/12/1 14:28
 */
@Document(indexName = "test_es")
@Routing(value = "@GenerateRouting.getRouting(#entity)")
@Setting(shards = 5)
public class TestESEntity implements Persistable<Long> {

    @Id
    @Field(name = "id", type = FieldType.Keyword)
    private Long id;

    @Field(type = FieldType.Keyword)
    private String name;

    @Field(type = FieldType.Text)
    private String nameChina;

    @Field(type = FieldType.Integer)
    private Integer updateField;

    /**
     * 可以支持java.util或者localDate、localDateTime
     *
     * @author dengmeiluan
     * @date 2021/12/1 14:30
     */
    @CreatedDate
    @Field(type = FieldType.Long)
    private Long createTime;

    @CreatedBy
    @Field(type = FieldType.Keyword)
    private String createBy;

    @LastModifiedDate
    @Field(type = FieldType.Long)
    private Long updateTime;

    @LastModifiedBy
    @Field(type = FieldType.Keyword)
    private String updateBy;

    /**
     * 用来表示是否是第一次
     *
     * @author dengmeiluan
     * @date 2021/12/1 14:29
     */
    @Transient
    private boolean first;

    @Override
    public Long getId() {
        return id;
    }

    /**
     * Persistable 实现方法
     *
     * @return boolean
     * @author dengmeiluan
     * @date 2021/12/1 14:29
     */
    @Override
    public boolean isNew() {
        return first;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameChina() {
        return nameChina;
    }

    public void setNameChina(String nameChina) {
        this.nameChina = nameChina;
    }

    public Integer getUpdateField() {
        return updateField;
    }

    public void setUpdateField(Integer updateField) {
        this.updateField = updateField;
    }

    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }

    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public Long getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Long updateTime) {
        this.updateTime = updateTime;
    }

    public String getUpdateBy() {
        return updateBy;
    }

    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }

    public boolean isFirst() {
        return first;
    }

    public void setFirst(boolean first) {
        this.first = first;
    }

    @Override
    public String toString() {
        return "TestESEntity{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", nameChina='" + nameChina + '\'' +
                ", updateField=" + updateField +
                ", createTime=" + createTime +
                ", createBy='" + createBy + '\'' +
                ", updateTime=" + updateTime +
                ", updateBy='" + updateBy + '\'' +
                ", first=" + first +
                '}';
    }
}

1.7Mapper定义

package com.example.demo.mapper;

import com.example.demo.entity.TestESEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * 可以写jpa类似的语法,可以进行查询,删除,保存,无法进行更新
 *
 * @author dengmeiluan
 * @date 2021/12/1 14:31
 */
public interface TestMapper extends ElasticsearchRepository<TestESEntity, Long> {

    long count();

    TestESEntity queryTestESEntityById(Long id);
}

1.8 Routing自定义

package com.example.demo.routing;

import com.example.demo.entity.TestESEntity;
import org.springframework.stereotype.Service;

/**
 * 自定义路由方法,在复杂的场景可能使用到,但是一般属性名即可
 *
 * @author dengmeiluan
 * @date 2021/12/1 14:31
 */
@Service(value = "GenerateRouting")
public class TestESGenerateRouting {

    /**
     * 获取路由值
     *
     * @param testEsEntity 实体
     * @return java.lang.String 路由值
     * @author dengmeiluan
     * @date 2021/12/1 14:32
     */
    public String getRouting(TestESEntity testEsEntity) {
        System.out.println("get into getRouting");
        return testEsEntity.getName();
    }
}

1.9启动类配置

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.elasticsearch.config.EnableElasticsearchAuditing;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

@SpringBootApplication
//如果不指定扫描包的路径,需要调用的bean上有@Respository
@EnableElasticsearchRepositories
//启用该方法即可以使用审计
@EnableElasticsearchAuditing
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

2.0测试类

package com.example.demo;

import com.example.demo.dao.TestESDAO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 测试类
 *
 * @author dengmeiluan
 * @date 2021/12/1 14:39
 * spring-boot-starter-elasticsearch 官方文档学习地址
 * @see 
 * elasticsearch官方文档学习地址
 * @see 
 * elasticsearch windowns 安装地址
 * https://www.elastic.co/cn/downloads/elasticsearch
 */
class DemoApplicationTests {

    @Autowired
    private TestESDAO testESDAO;

    @Test
    public void save() {
        testESDAO.save();
    }

    @Test
    public void updateSave() {
        testESDAO.updateSave();
    }

    @Test
    public void search() {
        testESDAO.search();
    }

    @Test
    public void deleteByQuery() {
        testESDAO.deleteByQuery();
    }

    @Test
    public void updateByQuery() {
        testESDAO.updateByQuery();
    }

    @Test
    public void updateById() {
        testESDAO.updateById();
    }

    @Test
    public void count() {
        testESDAO.count();
    }
}

2.1 POM文件



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.5.7
         
    
    com.example
    demo
    0.0.1-SNAPSHOT
    demo
    Demo project for Spring Boot
    
        1.8
        7.12.1
    
    
        
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
            2.5.7
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.projectlombok
            lombok
            RELEASE
            compile
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                2.2.5.RELEASE
            
        
    

完整代码路径

https://download.csdn.net/download/xuexi_deng/53381462

你可能感兴趣的:(java,elasticsearch,spring,java)