SSM框架整合ElasticSearch实现数据的增删改查实战案例

本文介绍如何使用Elasticsearch 5.5.3进行数据检索、批量导入、查询优化及高亮显示等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

     当处理海量数据时,单纯的mysql oracle 以及sql查询已经无法满足我们在效率上的需求,elasticSearch 是当下一款热门的实时搜索引擎基于lucense的搜索服务器,使用它可以完成近乎实时的数据查询。并且支持权重搜索,全文搜索等方式进行查询。

目录

一、准备开发环境

二、常用操作

三、查询结果高亮显示

四、效果展示​

五、后记:



一、准备开发环境

  • 该项目是基于Srping + SpringMVC+Mybatis的
  • 1.JDK 1.8
  • 2.elasticSearch5.5.1
  • 需要用到的相关依赖pom文件如下
      <dependency>  
                <groupId>org.elasticsearch</groupId>  
                <artifactId>elasticsearch</artifactId>  
                <version>5.5.3</version>  
      </dependency>  
       
       <!-- transport客户端 -->  
    <dependency>  
            <groupId>org.elasticsearch.client</groupId>  
            <artifactId>transport</artifactId>  
            <version>5.5.3</version>  
   </dependency>  
         <!--log4j-->    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version>
        </dependency>

 

   二、常用操作

  由于代码是从我的项目中拿出来的,没有用到的代码直接删除就可以了

  1.工具类ESutils,用于client客户端的关闭,创建

package com.rupeng.utlis;

import java.net.InetAddress;
import java.net.UnknownHostException;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import com.rupeng.service.config.ConfigInfo;

/**
 * 用来创建关闭es客户端
 * @author 2016wlw2 徐塬峰
 * 创建时间:2018年7月24日 上午10:32:28
 */
public class EsUtils {
	/**
	 * 獲取ElasticSearch
	 */
	public synchronized static  Client getClient() {
		Settings settings = Settings.builder().
				put("client.transport.sniff", true)
				.build();//自动嗅探其他集群的ip 如果有则加入
		InetSocketTransportAddress master = null;
		try {
			master = new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300);
			TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master);
			return client;
		} 
		catch (UnknownHostException e) {
			e.printStackTrace();
			throw new RuntimeException("elasticSearch Client init error 连接创建失败"+e);
		}
		}
	/**
	 * 用于关闭elasticSearch
	 */
	public static void closeClient(Client client){
		if(null != client){
			try {
				client.close();
			} catch (Exception e) {
				throw new RuntimeException("连接关闭失败");
			}
		}
	}
	
	

1.SSM注入client客户端

代码以及其引用的方式

/**
 * 实现自动注入elasticserach
 * @author Ray
 */
@Configuration
public class ElasticSerachConfig {
     //自动注入client 
	@Bean(name="client")
	public TransportClient esClint() throws UnknownHostException
	{
		Settings settings = Settings.builder().build();
		InetSocketTransportAddress master=new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300);	
		TransportClient client =new PreBuiltTransportClient(settings).addTransportAddress(master);
		return client;	
	}
}
        @Resource(name = "client")
	private TransportClient client;

2.基本操作(查询,增加,删除,修改)

2.1基本查询

public List<String> search (String query,int number)
    {
    	long start =System.currentTimeMillis();
        if(client==null)
        {
        	client =ESUtils.getTransportClient();
        }
//      定义查询用到的类型,单独定义
 	    String Type[]=new String[]{INDEXKEYTEMPLATE.TYPE_COURSES
 				,INDEXKEYTEMPLATE.TYPE_NEWS,INDEXKEYTEMPLATE.TYPE_SEGMENTS};
 	  
 	SearchRequestBuilder responsebuilder = client.prepareSearch(INDEX_NAME).setTypes(Type);
    	List<String> hitsList=new ArrayList<String>();  
 	String Key[]=new String[]{CoursesIndexKey.NAME,
                 NewsIndexKey.TITLE,
                 SegmentsIndexKey.NAME,
                 BbsIndexKey.TITLE   
 	    };//查询用到的关键字,单独定义
 	    SearchResponse myresponse = responsebuilder
    			.setQuery(QueryBuilders.multiMatchQuery(query, Key))
    			.setFrom(0)//from 从哪条开始 ,可用于分页操作
    			.setSize(number).setExplain(true).execute().actionGet();//number为查询的条数
 	   
    	SearchHits hits = myresponse.getHits();
    	for (int i = 0; i < hits.getTotalHits(); i++) // getHits()当前查询页的结果
    	{
    		SearchHit hit = hits.getHits()[i];			
    		String jsonStr=hit.getSourceAsString();
            logger.info("索引库的数据:" +jsonStr );  
            hitsList.add(jsonStr);
    		
    	}	  	
    	long end=System.currentTimeMillis();
    	logger.debug("为您找到相关结果约"+hits.getTotalHits()+"个----"+"耗时"+(end-start)/1000+"秒");
	return hitsList;
    }

2.2根据id来获取数据

 
    public String getOneById(String INDEX_TYPE,Long id)
    {  

	    if(client==null)
            {
        	client =ESUtils.getTransportClient();
            }
		  GetResponse getResponse = client.prepareGet(INDEX_NAME,INDEX_TYPE,String.valueOf(id))  
	                                     .execute()  
	                                     .actionGet();  
	    if(getResponse!=null)
            {
          	 logger.debug(getResponse.getSourceAsString());
          	 String jsonStr=getResponse.getSourceAsString();
          	 return jsonStr;
            }
            else
            {
          	logger.error("找不到id对应的数据");
 			return null;  
            }
           
	 }  
	    

2.3精确匹配 方法和1类似 不过替换为MatchPhraseQuery

2.4创建操作 分批次提交数据

基本思路,从数据库获取数据,并将数据库里的数据插入到elasticSearch搜索服务器,数据量由于比较大,所以

采用了分批次提交

	public boolean createAll(List<T>  pojoList,String INDEX_TYPE) throws InterruptedException, ExecutionException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException 
	{			
		long start =System.currentTimeMillis();
		Gson gson = new Gson();	
                if(client==null)
                {
         	client =ESUtils.getTransportClient();
                }
		BulkRequestBuilder bulkRequest = client.prepareBulk(); 	
		for(T pojo:pojoList)
		{
//			通过反射获得方法getID 设置主键
//			pojo.getClass().getMethod("getId", Long.class);
			Field FieldId=pojo.getClass().getDeclaredField("id");
		        //设置可读写
			FieldId.setAccessible(true);
			Object id = FieldId.get(pojo);   				
			IndexRequest request=client.prepareIndex(INDEX_NAME,INDEX_TYPE).setId(String.valueOf(id))
					.setSource(gson.toJson(pojo),XContentType.JSON).request();
			bulkRequest.add(request);
			if(bulkRequest.numberOfActions()==1000)
			{
				   bulkRequest.execute().get();//get的实现就是调用executeget	
				   bulkRequest=client.prepareBulk();//创建一个对象	
			           logger.debug("已提交1000条数据");

			}				
		}		
		if(bulkRequest.numberOfActions()>0)//最后一批不足一千的再提交一次
	         {	
	       bulkRequest.execute().get();//
	       long end=System.currentTimeMillis();
	       logger.debug("最后一批已提交,"+"耗时"+(end-start)/1000+"秒");

	        }
		
		ESUtils.close(client);
    	        return true;		
   
    }

2.6删除操作

2.6.1删除整个索引库

            /** 注意 :该操作会删除整个索引库
	     * 
	     * @param INDEX_NAME
	     */
	    public void deleteIndex(String INDEX_NAME)  
	    {  
	    	
	    	 if(client==null)
	            {	              
	            	client =ESUtils.getTransportClient();
	            }
	    TransportResponse resp=client.admin().indices().prepareDelete(INDEX_NAME).get();  
            if(resp!=null)
            {
	  	    logger.debug("索引库删除成功");
	  	    ESUtils.close(client);

            }
            else
            {
            logger.error("索引删除失败");
	  	    ESUtils.close(client);
            }
 	    ESUtils.close(client);
	    
	    }  

2.6.2 删除指定id

如果需要删除单个类型里的多个文档,则可以使用for循环删除 或者其他 只要思路正确就ok

/**
		 * 根据id的值删除文档
		 * @param id
		 * @param INDEX_TYPE
		 * @throws UnknownHostException
		 */
		 public void removeOneById(Long id,String INDEX_TYPE)
		 {	   
			if(client==null)
	                {	                
	            	client =ESUtils.getTransportClient();
	                 }
		        DeleteResponse response = client.prepareDelete(INDEX_NAME, INDEX_TYPE, String.valueOf(id)).get();
		        String index = response.getIndex();
		        String type = response.getType();
		        String typeId = response.getId();
		        long version = response.getVersion();
		        logger.debug("删除一条文档"+index + " : " + type + ": " + typeId + ": " + version);
		        ESUtils.close(client);
		     
		 }

2.6.3 删除指定分类

public boolean deleteType(String indexName, String type) {
		client = EsUtils.getClient();
		QueryBuilder builder = QueryBuilders.typeQuery(type);
		DeleteByQueryAction.INSTANCE.newRequestBuilder(client).source(indexName).filter(builder).execute().actionGet();
		EsUtils.closeClient(client);
		return true;
	}

 

2.7插入一条数据

                  /**
		  * 创建一条或者更新索引
		  * @param esMixedDataDto
		  * @return
		  */
		 public boolean upsertDocument(T pojo,String INDEX_TYPE) {
		     client =ESUtils.getTransportClient();
	             Gson gson = new Gson();				 
		     Field FieldId;
			try {
				
		         FieldId = pojo.getClass().getDeclaredField("id");		 
			 FieldId.setAccessible(true);
			 Object id = FieldId.get(pojo);  
			 IndexResponse indexRes=client.prepareIndex(INDEX_NAME,INDEX_TYPE).setId(String.valueOf(id))
					.setSource(gson.toJson(pojo),XContentType.JSON).get();
		 	String index = indexRes.getIndex();
	        String type = indexRes.getType();
	        String typeId = indexRes.getId();
	        logger.debug("插入成功"+index + " : " + type + ": " + typeId + ": " );
	        ESUtils.close(client);
	        return true;
			
			}
    	 	 catch (NoSuchFieldException | SecurityException e) {
				// TODO Auto-generated catch block
		 		logger.warn("插入失败");
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
		 		logger.warn("插入失败");
				e.printStackTrace();
			} catch (IllegalAccessException e) {
		 		logger.warn("插入失败");
				e.printStackTrace();
			}
			return false;
 	 
		    }

三、查询结果高亮显示

java代码实现高亮显示

 public Map<String,Object> hlSearch (String query,int number)
    {
    	long start =System.currentTimeMillis();
        if(client==null)
        {
        	client =ESUtils.getTransportClient();//创建连接
        }
 	    String Type[]=new String[]{INDEXKEYTEMPLATE.TYPE_COURSES
 				,INDEXKEYTEMPLATE.TYPE_NEWS,INDEXKEYTEMPLATE.TYPE_SEGMENTS};
 	  
 	    SearchRequestBuilder requestBuilder = client.prepareSearch(INDEX_NAME).setTypes(Type);
 	    String Key[]=new String[]{
 	    		 CoursesIndexKey.NAME,
                 NewsIndexKey.TITLE,
                 SegmentsIndexKey.NAME,
                 BbsIndexKey.TITLE,
 	    };
 	   SearchRequestBuilder  searchRequestBuilder = requestBuilder
    			.setQuery(QueryBuilders.multiMatchQuery(query, Key))
    			.setFrom(20). //从那行开始
    			setSize(30).setExplain(true);//.actionGet()==get()
 	   
        Map<String,Object> msgMap = new HashMap<String,Object>();  
        HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);  
        highlightBuilder.preTags("<span style=\"color:red\">");  
        highlightBuilder.postTags("</span>");  
        searchRequestBuilder.highlighter(highlightBuilder);  
        SearchResponse response = searchRequestBuilder.get();  
        List<Map<String,Object>> result = new ArrayList<>();  
        
        
        
    	for (SearchHit hit:response.getHits()) // getHits()当前查询页的结果
    	{
            Map<String, Object> source = hit.getSource();  
    		//创建map 存放高亮字段 并返回到对象中
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();  
             //处理高亮字段
            HighlightField nameField = highlightFields.get("title");  
            if(nameField!=null){  
                
            	Text[] fragments = nameField.fragments();  
                String nameTmp ="";  
                for(Text text:fragments){  
                    nameTmp+=text;  
                    source.put("title",nameTmp);
                    source.put("content",nameTmp);
                }  
                source.put("title",nameTmp);

            }
            result.add(source);  
    }
    	//封装数据返回  
        msgMap.put("itemsList",result);     //搜索结果  
        //msgMap.put("page","page");          //分页  
        msgMap.put("took",response.getTook().getSecondsFrac()); //获取响应需要的时间  
        return msgMap;  
    }

Controller层

   @RequestMapping(value="/hlsearch.do",method=RequestMethod.POST)
   public @ResponseBody AjaxResult hlsearch(String text) 
   {
	   Map<String,Object> msg=searchService.hlSearch(text, 10);
	   for(Map.Entry<String, Object> entry :msg.entrySet())
	   {
		   System.out.println("Key:"+entry.getKey()+"Value:"+entry.getValue());		   
	   }
	   return new AjaxResult("Success",msg);	   
   }

前端代码(vue.js)

search:function()
                    {
                        var that=this;
                        that.results=[];                                            
                        axios.post('./hlsearch.do?text='+this.text)
                                .then(function(resp){
                                   
                                	var results=resp.data.data.itemsList;
                                    if(results.length==0)
                                    {
                                    	alert("未找到匹配结果");                                  
                                    	that.message="<font size='2' color='CCCCCC'>找到约0 条结果!</font>";
                                       
                                    }                                  
                                    else
                                    {
                                    that.message="<font size='2' color='CCCCCC'>找到约 "+results.length+" 条结果!</font>";
                                    for(var i=0;i<results.length;i++)
                                    {
                                        that.results.push(resp.data.data.itemsList[i]);
                                        
                                    }
                                    }
                                })
                                .catch(function(error){alert("ajax错误"+error);})
                    }}

 

四.效果展示

70

 

五、后记:

1.前端不要用ajax请求来获取数据,异步获取数据在并发环境下 会导致前端展示结果 错乱的情况,所以

建议大家使用返回视图的方式来展示数据,这样效果更好

2.生产环境下 client必须关闭,否则会导致资源泄露,内存溢出等问题。但是client 获取连接的时间较慢依然是一个大问题

获取一个client平均需要2秒左右 ,这样查询快的意义就没有了。所以应该尽量采用es连接池的方式来创建client 客户端。

连接池代码(参考)

package com.rupeng.elasticpools;

import java.net.InetAddress;

import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import com.rupeng.service.config.ConfigInfo;

/**
 * 每一次创建es的连接都太耗费时间 我们应该自己去封装一个es的连接池
 * @author 2016wlw2 徐塬峰 创建时间:2018年7月31日 上午10:00:32
 */
public class ElasticSearchFactory implements PooledObjectFactory<Client>{

	private  int elasticPool_maxTotal = Integer.parseInt(ConfigInfo.elasticPool_maxTotal.trim());
	private  int elasticPool_maxIdle =  Integer.parseInt(ConfigInfo.elasticPool_maxIdle.trim());
	private  String elasticSearch_addr ="127.0.0.1";// ConfigInfo.elasticSearch_addr;
	private  int elasticSearch_port = Integer.parseInt(ConfigInfo.elasticSearch_port.trim());
	private  int elasticPool_minIdle =  Integer.parseInt(ConfigInfo.elasticPool_minIdle.trim());
	private  int maxWaitMilis = Integer.parseInt(ConfigInfo.elasticPool_maxWaitMilis.trim());
	
	private  static GenericObjectPool<Client> pool;// 连接池
	static
	{
		ElasticSearchFactory fa=new ElasticSearchFactory();
		fa.createElasticSearchPool();
		
	}
	/**
	 * 创建连接池
	 */
	public void createElasticSearchPool()
	{
		ElasticSearchFactory fac = new ElasticSearchFactory();// 创建工厂
		GenericObjectPoolConfig conf = new GenericObjectPoolConfig();// 配置文件
		conf.setMaxTotal(elasticPool_maxTotal);// 设置线程池中最大的数量
		conf.setMaxIdle(elasticPool_maxIdle);// 设置最大的空闲时间
		conf.setMinIdle(elasticPool_minIdle);// 设置最小空闲连接
		conf.setMaxWaitMillis(maxWaitMilis);// 设置最大等待时间
		
		try {
			//先创建三个连接
			fac.makeObject();
			fac.makeObject();
			fac.makeObject();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		pool = new GenericObjectPool<Client>(fac, conf);// 创建连接池
		
	}
	// 模拟对象创建的过程
	@Override // 通过ElastcSearchConnections 来创建连接
	    public PooledObject<Client> makeObject() throws Exception {
		Settings settings = Settings.builder().put("client.transport.sniff", true).build();// 自动嗅探其他集群的ip
		InetSocketTransportAddress master = null;
		master = new InetSocketTransportAddress(InetAddress.getByName(elasticSearch_addr), elasticSearch_port);
		TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master);
		System.out.println("太棒了,对象创建成功了!");
		return new DefaultPooledObject<Client>(client);
	}
	
	public static GenericObjectPool<Client> getPool() {
		return pool;
	}
	
	//销毁
	@Override
	public void destroyObject(PooledObject<Client> p) throws Exception {
             p.getObject().close();
	}
	@Override
	public boolean validateObject(PooledObject<Client> p) {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public void activateObject(PooledObject<Client> p) throws Exception {
		
	}
	@Override
	public void passivateObject(PooledObject<Client> p) throws Exception {
	    System.out.println("passivate Object"+p.toString());

	}
	
	


}

3.elasticSearch 已不再维护transport 客户端 尽量使用rest client客户端来进行开发。。。。。。。

 

 

以上就是我归纳的对elasticsearch5.5.3版本的java实现,如果有哪些地方有问题,欢迎大家留言指正,感激不尽!

 

 

【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎ElasticSearch的电影搜索系统项目源码.zip 基于SSM架构结合全文搜索引擎Elastic
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值