Spring-data +elasticsearch 2.4.4 整合搭建指南

博客被黑,天杀的把我置顶的精华文章给干掉了。幸好有人复制了此文,以得部分恢复。现在Spring-data 2.0.1已经release了,等有空升级至spring-data 2.0版本。

Spring-data +elasticsearch 2.4.4 整合搭建指南

最后更新: 17-3-24

1. 简介

spring data是一个统一包括数据库系统和NoSQL数据存储在内不同持久化存储框架,旨在为jpa和nosql的存储框架提供统一接口,减轻开发难度。

Elasticsearch 是一个基于Lucene的搜索引擎框架,为啥是它而不是solr?虽然solr搜索性能但是在单节点读写并发的时候IO性能不如Elasticsearch,所以有整合Elasticsearch的需求。

剩余详细的介绍就不再赘述,自己百度,来点干货,节约篇幅,低碳环保。

2. 准备

2.1 版本条件

首先再次吐个槽(之前写过一篇吐槽了),对于最新的elasticsearch 5.X的版本目前spring-data尚未支持,无法使用最新特性,因此最高只能使用elasticsearch 2.x的版本2.4.4。笔者使用的各版本参考如下:

组件 版本
spring framework 4.3.2
spring-data-commons 1.12.2
spring-data-elasticsearch 2.0.6
elasticsearch 2.4.4

其它附加的包可以从elasticsearch 官方网站和spring-data网站下载zip获得 
https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/zip/elasticsearch/2.4.4/elasticsearch-2.4.4.zip

https://github.com/spring-projects/spring-data-elasticsearch/archive/2.0.6.RELEASE.zip

2.2 配置好elasticsearch独立节点服务

要跑起来elasticsearch很简单,上面的下载的目录解压缩后,在解压目录bin找到elasticsearch.bat,头部加上一行你的JDK安装目录:

set JAVA_HOME=c:\jdk1.8
 
 
  • 1
  • 1

然后双击运行即可。如果你能看到

[INFO ][node                     ] [Scourge of the Underworld] started
 
 
  • 1
  • 1

的字样说明已经运行起来了,窗口不要关掉。如果还不行,先解决启动问题(无非就是端口占用或启动了多个之类)

3. 开始整合

3.1. step1 配置客户端

elasticsearch的客户端配置比较简单。首先在头部申明命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-4.3.xsd
         http://www.springframework.org/schema/mvc
         http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
         http://www.springframework.org/schema/data/jpa
         http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
         http://www.springframework.org/schema/data/elasticsearch
         http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd
       "
       default-autowire="byName" default-lazy-init="false">
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

然后申明elasticsearch的client接入,elasticsearch的client接入有两种模式。一种是embedded,一种是独立服务的方式也是elasticsearch官方建议的方式:

    <!-- 远程机器 -->
    <elasticsearch:transport-client id="client" cluster-nodes="${app.elasticsearch.address:localhost:9300,localhost:9300}" cluster-name="elasticsearch" /> 

    <!-- embed模式client,数据采用UUID的方式每运行一次重置一次数据,对应data目录需要手动清空
    <elasticsearch:node-client id="client" local="true" 
    cluster-name="#{T(java.util.UUID).randomUUID().toString()}" 
    http-enabled="false"
    path-data="#{
        T(org.ccframe.commons.util.WebContextHolder).getWarPath() + T(java.io.File).separator + 'WEB-INF' + T(java.io.File).separator
        + 'eshome' + T(java.io.File).separator + 'esdata'
    }"
    path-home="#{
        T(org.ccframe.commons.util.WebContextHolder).getWarPath() + T(java.io.File).separator + 'WEB-INF' + T(java.io.File).separator
        + 'eshome'
    }"
    /> -->
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

cluster-nodes数据为你远程的elastic服务的端口,注意不是elasticsearch管理的端口。elasticsearch节点管理端口默认用9200,这里要写9300。可以配置一个或多个节点,用逗号分隔。 
cluster-name是你端口运行节点的集群名称。注意要和你节点的elasticsearch.yml里配置的cluster-name一致,默认是elasticsearch。

embedded模式可以用于本地开发和测试。与独立节点的区别就是local为true,不用配置cluster-nodes而改为配置path-home和path-data。path-home需要指向你本地的路径,你可以自己写个Filter抓取ServletContext里的ContextPath来获得绝对路径,具体实现不在本文讨论。

3.2. step2 编写你的实体类

编写你用于存储elasticsearch数据的实体类,先看代码片段:

import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Setting;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

//elasticsearch
@Document(indexName = "article_inf_index", type = "articleInf")
@Setting(settingPath = "elasticsearch-analyser.json")
pblic class ArticleInf implements Serializable{

    //elasticsearch
    @org.springframework.data.annotation.Id
    private Integer articleInfId;

    @Field(type = FieldType.String, analyzer="ngram_analyzer")//使用ngram进行单字分词
    private String articleTitle;

    @Field(type = FieldType.Date, store = true, format = DateFormat.custom, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
    @JsonFormat (shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
    private Date releaseTime;

    public Date getReleaseTime() {
        return releaseTime;
    }

    public void setReleaseTime(Date releaseTime) {
        this.releaseTime = releaseTime;
    }

    public String getArticleTitle() {
        return articleTitle;
    }


    public void setArticleTitle(String articleTitle) {
        this.articleTitle = articleTitle;
    }

}

 
 
  • 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
  • 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

elasticsearch实体的申明主要靠那几个注解。我逐一做个简单介绍:

@Document 标识了该实体用于elasticsearch,indexName和type必须指定。indexName为save时保存到节点的索引名称,type为你的保存的类型(废话,百度翻译的吧-_-b,要真全面理解index和type还真不是 
一两句话能解释的,这是elasticsearch的知识,现在为了整合,先来点不求甚解)

@Setting 可以省略,但是如果你要进行单字搜索等处理,需要配置索引的setting,这就需要指定配置的位置。settingPath指定的路径可以用fullPath,如果用相对路径则是你的classpath?(不知道配置在哪,简单说一般是你的log4j.properties相同的目录,没有log4j?我…) 
elasticsearch-analyser.json的格式请注意,是settings子对象的结构,下面是一个例子:

{
    "index":{
        "number_of_shards":1,
        "number_of_replicas":0,
        "analysis":{
            "tokenizer":{
                "ngram_tokenizer":{
                    "type":"nGram",
                    "min_gram":1,
                    "max_gram":20
                }
            },
            "analyzer":{
                "ngram_analyzer":{
                    "type":"custom",
                    "tokenizer":"ngram_tokenizer"
                }
            }
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

例如上面这个就是配置ngram分词,让你建立数据”CHINA2017”时能通过”NA20”搜索到内容,和数据库like模式完全一致。

@org.springframework.data.annotation.Id 用于标记elasticsearch mapping里的_id。没有ID是保存不了的。

@Field 可以对mappings做一些额外的配置,例如指定数据的分词器设置ngram或ik、paoding等,还可以设置index的store等属性,具体你可以搜索一下elasticsearch的mappings配置文章,在此不再赘述(没办法,知识联系大,百度一下很容易的,限于篇幅,这里就埋个引子)。注意一下如果要实现时间的排序和范围搜索,还要额外指定下@JsonFormat,可以参考例子的代码。

其它类型如果不用额外指定mapping的,就是annotation Field的默认属性:

public @interface Field {

    FieldType type() default FieldType.Auto;

    FieldIndex index() default FieldIndex.analyzed;

    DateFormat format() default DateFormat.none;

    String pattern() default "";

    boolean store() default false;

    String searchAnalyzer() default "";

    String analyzer() default "";

    String[] ignoreFields() default {};

    boolean includeInParent() default false;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.3. step3 编写CRUD支持

先写一个接口,让它支持普通的CRUD和分页。

public interface BaseSearchRepository<E, ID extends Serializable> extends ElasticsearchRepository<E, ID>, PagingAndSortingRepository<E, ID>{
}
 
 
  • 1
  • 2
  • 1
  • 2

然后写你自己的repository类:

public interface ArticleInfSearchRepository extends BaseSearchRepository<ArticleInf, Integer>{}
 
 
  • 1
  • 1

然后还可以写个service注入你要的接口,还有很多方法可以扩展,限于篇幅,你可以看下ElasticsearchRepository和PagingAndSortingRepository接口

public abstract class BaseSearchService <E,ID extends Serializable,R extends BaseSearchRepository<E,ID>>{ //spring 4.X 支持泛型注入

    private Logger log = Logger.getLogger(this.getClass());

    private R repository;

    @Autowired
    public void setRepository(R repository) {
        this.repository = repository;
    }

    protected R getRepository(){
        return repository;
    }

    public E getById(ID id) {//
        return getRepository().findOne(id);
    }

    public Iterable<E> listAll() {
        return getRepository().findAll();
    }

    public void save(E data){
        getRepository().save(data);
    }

    public void delete(E data){
        getRepository().delete(data);
    }

    public void deleteById(ID id){
        getRepository().delete(id);
    }

    public E getByKey(String fieldName, Object value){
        try{
            return getRepository().search(QueryBuilders.matchQuery(fieldName, value)).iterator().next();
        }catch(NoSuchElementException e){
            return null;
        }
    }

}
 
 
  • 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
  • 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

然后你的业务service类:

public class ArticleInfSearchService extends BaseSearchService<ArticleInf, Integer, ArticleInfSearchRepository>{}
 
 
  • 1
  • 1

然后再啰嗦一下,如果你使用基于注解配置的话,注意配置package-scan,注意配置package-scan,注意配置package-scan,重要的话说3遍。

    <!-- spring-data的ElasticSearch集成 -->
    <elasticsearch:repositories base-package="org.**.search" />

 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

3.4. step4 编写搜索

spring-data默认是基于接口的方式,然后会自动帮你生成需要的类。那么elasticsearch的整合也不例外(体会一下统一接口编写方式的好处)。例如一个简单的搜索:

public interface ArticleInfSearchRepository extends BaseSearchRepository<ArticleInf, Integer>{
public List<ArticleInf> findByArticleTitle(String articleTitle);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

还可以用@Query指定你的查询语句,query怎样写你得看elasticsearch的语法。

@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
    Page<Book> findByName(String name,Pageable pageable);
 
 
  • 1
  • 2
  • 1
  • 2

除了按名称和按Query外,再复杂点就要用到QueryBuilder,接口基类有2个方法用到QueryBuilder来动态构建查询:

    Iterable<T> search(QueryBuilder query);
    Page<T> search(QueryBuilder query, Pageable pageable);
 
 
  • 1
  • 2
  • 1
  • 2

QueryBuilder看官方也没什么特别的,对照词霸就能搞定,这里列举几个从sql刚转过来的常见问题:

  1. 用match默认无法对英文做部分模糊匹配,要配置ngram,前面提过一次;
  2. 进行多条件查询先建立个BoolQueryBuilder,然后再boolQueryBuilder.must来添加and条件;
  3. boolQueryBuilder的or条件叫should,boolQueryBuilder.should(….);

更复杂查询可以参考官方例子: 
http://docs.spring.io/spring-data/elasticsearch/docs/2.0.6.RELEASE/reference/html/#repositories.query-methods

3.5. step5 spring事务的整合

写到这里各位码哥码弟们是不觉得还少了点什么不完美?好吧也不卖关子了。上面标题已经写的很清楚。 
当你保存数据库失败,或出现业务异常回滚时,你的搜索事务该咋办。这里处理不好,就会出现数据库保存进去,但是搜索却多了一条数据的情况,那就麻烦大了。

有经验的同学会想到事务,可惜: 
Elasticsearch doesn’t support transactions!Elasticsearch doesn’t support transactions!Elasticsearch doesn’t support transactions! 
重要的话说3遍

好吧,正规的方式实现不了,我们可以采用点变通的方法,将所有的操作搜集起来,然后在事务结束时统一提交,这样如果有异常回滚,所执行的操作也不会提交到数据库。

但这样做有个缺点,如果一个事务内后面的逻辑依赖前面的ES提交的结果,那么解决的方式和数据库操作一样——flush。将积累的操作立即写入到索引,然后等待个1S(ES通常1秒内完全能建立好)就能获取到索引后的数据了。

下面给个自己写的工具类,实现类似的操作,但仅限于以下场景:

  • ES和jpa共用对象
  • 抽离BaseSearchService来处理ES的基本操作和抽离BaseService来处理JPA基础操作

    没提供更多的代码,你就当伪代码来读吧:)

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.xxxxx.client.Global;
import org.xxxxx.commons.base.BaseSearchService;
import org.xxxxx.commons.helper.SpringContextHelper;

@SuppressWarnings("rawtypes")
public class ElasticsearchTransactionUtil {

    /**
     * 线程操作队列
     */
    private static ThreadLocal<List<Operation>> operationListLocal = new ThreadLocal<List<Operation>>();

    /**
     * 启动事务
     */
    public static void init(){
        operationListLocal.set(new ArrayList<Operation>());
    }

    /**
     * 将对象操作保存到事务
     */
    private static void innerPushOperation(Class jpaServiceClass, Action action, Serializable data, boolean needClone){
        String beanName = StringUtils.uncapitalize(jpaServiceClass.getSimpleName().replace(Global.SERVICE_CLASS_SUFFIX, Global.SEARCH_SERVICE_CLASS_SUFFIX)); //根据名称匹配找到xxxSearchService的springBean
        if(!SpringContextHelper.containsBean(beanName)){
            return;
        }
        Object bean = SpringContextHelper.getBean(beanName);
        if(bean instanceof BaseSearchService){
            operationListLocal.get().add(new Operation((BaseSearchService)bean, action, needClone? (Serializable)SerializationUtils.clone(data) : data)); //将操作添加到队列,对当时保存的对象做浅克隆快照
        }
    }

    public static void pushSave(Class jpaServiceClass, Serializable data){
        innerPushOperation(jpaServiceClass, Action.SAVE, data, true);
    }

    public static void pushDelete(Class jpaServiceClass, Serializable data){
        innerPushOperation(jpaServiceClass, Action.DELETE, data, true);
    }

    public static void pushDeleteById(Class jpaServiceClass, Serializable id){
        innerPushOperation(jpaServiceClass, Action.DELETE_BY_ID, id, false);
    }

    public static enum Action{
        SAVE, DELETE, DELETE_BY_ID 
    }

    /**
     * 需要flush时 也调用此方法
     */
    public static void commit(){
        List<Operation> operationList = operationListLocal.get();
        for(Operation operation: operationList){
            switch(operation.getAction()){
                case SAVE:
                    operation.getSearchService().save(operation.getData()); //执行保存
                    break;
                case DELETE:
                    operation.getSearchService().delete(operation.getData()); //执行删除
                    break;
                case DELETE_BY_ID:
                    operation.getSearchService().deleteById(operation.getData()); //执行删除
                    break;
                default:
                    break;
            }
        }
        operationList.clear();
    }

    public static void rollback(){
        operationListLocal.get().clear();
    }

    /**
     * 索引操作对象.
     * @author JIM
     */
    private static class Operation{
        public Operation(BaseSearchService searchService, Action action, Serializable data){
            this.searchService = searchService;
            this.action = action;
            this.data = data;
        }

        private Action action;


        public Action getAction() {
            return action;
        }

        public void setAction(Action action) {
            this.action = action;
        }

        private Serializable data;

        public Serializable getData() {
            return data;
        }

        public void setData(Serializable data) {
            this.data = data;
        }

        private BaseSearchService searchService;

        public BaseSearchService getSearchService() {
            return searchService;
        }

        public void setSearchService(BaseSearchService searchService) {
            this.searchService = searchService;
        }

    }
}

 
 
  • 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
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 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
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

然后扩展下JpaTransactionManager,在原来的事务动作上添加你自己的动作

public class MyTransactionManager extends JpaTransactionManager {

    /**
     * 
     */
    private static final long serialVersionUID = -3878501009638970644L;

    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        super.doBegin(transaction, definition);
        if(!definition.isReadOnly()){ //只读事务无需操作索引
            ElasticsearchTransactionUtil.init();
        }
    }

    @Override
    protected void doCommit(DefaultTransactionStatus status) {
        super.doCommit(status);
        if(!status.isReadOnly()){ //只读事务无需操作索引
            ElasticsearchTransactionUtil.commit();
        }
    }

    @Override
    protected void doRollback(DefaultTransactionStatus status) {
        if(!status.isReadOnly()){ //只读事务无需操作索引
            ElasticsearchTransactionUtil.rollback();
        }
    }
}
 
 
  • 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
  • 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

3.6. step6 结束

到此我觉得就结束了,至于怎样注入到service去使用应该是spring架构的学习问题。在此不多扯,做一个纯粹的人,一个有益于码农的人(怎么感觉像哪篇小学课文?)

后面真没有了。

【完】

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
spring-data-elasticsearch官方教程文档提供了关于如何使用spring-data-elasticsearch框架进行Elasticsearch数据库操作的详细指导。该文档分为几个部分,包括介绍、快速入门、基本概念、查询构造器和复杂查询等。 文档的介绍部分简要介绍了spring-data-elasticsearch框架以及它提供的主要功能。快速入门部分通过一个简单的示例演示了如何配置spring-data-elasticsearch并进行基本的CRUD操作。该示例涵盖了创建索引、映射实体类、保存数据、查询数据以及删除数据。通过这个示例,读者可以迅速了解到spring-data-elasticsearch的基本用法。 基本概念部分详细介绍了spring-data-elasticsearch中的一些重要概念,包括实体类映射、索引操作、文档操作、字段映射、分页和排序等。这些概念对于深入理解spring-data-elasticsearch的使用非常重要。 查询构造器部分介绍了spring-data-elasticsearch提供的查询构造器的使用方法。该部分通过实例演示了如何使用查询构造器进行基本查询、范围查询、模糊查询以及布尔查询等。读者可以通过这些示例快速掌握查询构造器的使用。 最后一部分是关于复杂查询的介绍。这一部分介绍了如何使用原生查询、使用注解进行查询以及使用自定义实现进行查询等。复杂查询是spring-data-elasticsearch一个非常重要的特性,通过它可以实现更加灵活和高级的查询功能。 总体来说,spring-data-elasticsearch官方教程文档提供了丰富的实例和详细的说明,对于想要学习和使用spring-data-elasticsearch框架的开发者来说,是一份非常有价值的指南。读者可以通过该文档逐步了解spring-data-elasticsearch的相关概念和基本用法,并通过实例演示快速上手。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值