springboot+ElasticsearchRestTemplate 实现DSL日志打印

本文介绍了如何在SpringDataElasticsearch的ElasticsearchRestTemplate中,通过Java反射机制获取并打印ES查询的DSL语句,以便于开发和问题追踪。作者展示了如何扩展CustomTemplate类来实现实时日志功能。
摘要由CSDN通过智能技术生成

需求说明

近期项目碰到一个问题,需要使用ES中的java api中的ElasticsearchRestTemplate,调用
org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate#search方法,需要实现DSL语句日志打印;

调研阶段

其中跟踪源码发现

@Override
	public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
		SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
		SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));

		ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
		SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);

		return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
	}

在SearchRequest 这个对象中,有一个source的属性,包含了去es请求的完整dsl语句;
为了获取他,接着发现一个问题:

protected RequestFactory requestFactory;

RequestFactory为私有类,无法直接调用,虽然ElasticsearchRestTemplate提供了getRequestFactory()这个方法,但依然无法编译运行

public RequestFactory getRequestFactory() {
		return requestFactory;
	}

解决方案

去拿到这个requestFactory,目前有且仅有的方法就是通过Java反射的方法,然后实现了拿到
SearchRequest 这个对象;

 Method searchRequestMethod = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class);
                searchRequestMethod.setAccessible(true);
                SearchRequest searchRequest = (SearchRequest) ReflectionUtils.invokeMethod(searchRequestMethod, getRequestFactory(), query, clazz, index);

接着通过代码提示发现没有source的get关键字的方法,但实际上还是有提供同类的方法,只不过没有get关键字而已

 public SearchSourceBuilder source() {
        return source;
    }

好了,接下来该有的准备都有了,只需要对ElasticsearchRestTemplate进行扩展即可,以下是实现代码:

package com.example.methodtest;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponseBuilder;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;

/**
 * 实现dsl日志打印
 *
 * @author AaronCgt
 * @since 2024/3/12/012
 */
@Component
@Slf4j
public class CustomTemplate extends ElasticsearchRestTemplate {


    public CustomTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
        super(client, elasticsearchConverter);
    }

    public <T> SearchHits<T> search(Query query, Class<T> clazz, boolean isDebug)  {
        IndexCoordinates index=getIndexCoordinatesFor(clazz);
        try {
            if (!isDebug){
                return search(query,clazz,index);
            }else{
                Method searchRequestMethod = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class);
                searchRequestMethod.setAccessible(true);
                SearchRequest searchRequest = (SearchRequest) ReflectionUtils.invokeMethod(searchRequestMethod, getRequestFactory(), query, clazz, index);
                log.info("DSL:{}", searchRequest.source().toString());

                SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));

                ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
                SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);

                return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
}

至此,需求实现。可以根据实际需求,调用这个类,进行日志打印,例如平时开发接口,预留打印日志参数,需要时传入,实现接口级别DSL语句打印,也可以用于生产坏境出现问题时候对DSL的复现和跟踪!

jar包版本说明

为了准确性,以下提供jar包版本仅供各位参考:
在这里插入图片描述

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot中使用JPA动态表名需要使用DSL(Domain Specific Language)查询来实现DSL查询是一种基于对象的查询语言,可以根据运行时条件生成动态的SQL查询。 要实现动态表名,首先需要定义一个通用的实类,该实类包含了所有可能的表字段。然后根据运行时条件,使用DSL查询构建动态的表名。 以下是一个示例,演示了如何在Spring Boot中使用JPA动态表名: 1. 定义通用的实类: ```java @Entity @Table(name = "generic_table") public class GenericEntity { // 定义通用的表字段 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 其他字段... // getter和setter方法 } ``` 2. 创建动态表名的接口: ```java public interface DynamicTableName { @Query(value = "SELECT * FROM :tableName", nativeQuery = true) List<GenericEntity> findByDynamicTableName(@Param("tableName") String tableName); } ``` 3. 创建实现动态表名接口的类: ```java @Repository public class DynamicTableNameImpl implements DynamicTableName { @PersistenceContext private EntityManager entityManager; @Override public List<GenericEntity> findByDynamicTableName(String tableName) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<GenericEntity> query = criteriaBuilder.createQuery(GenericEntity.class); Root<GenericEntity> root = query.from(GenericEntity.class); // 动态设置表名 root.alias(tableName); query.select(root); TypedQuery<GenericEntity> typedQuery = entityManager.createQuery(query); return typedQuery.getResultList(); } } ``` 4. 在Service或Controller中使用动态表名查询: ```java @Service public class YourService { @Autowired private DynamicTableName dynamicTableName; public List<GenericEntity> findByDynamicTableName(String tableName) { return dynamicTableName.findByDynamicTableName(tableName); } } ``` 在以上示例中,通过将动态表名作为参数传递给findByDynamicTableName()方法,可以实现根据不同的条件查询不同的表。注意,这里使用了原生查询(nativeQuery = true)来执行SQL查询。 希望以上示例对你有帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值