1. 测试Api连接

1.1. 添加证书

  • 添加已导出的证书到src/main/resources/certificate/http_ca.crt

1.2. 添加依赖

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

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

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

	<!--
		<dependency>
		    <groupId>org.elasticsearch.plugin</groupId>
		    <artifactId>x-pack-sql-jdbc</artifactId>
		    <version>8.11.2</version>
		</dependency>

		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		</dependency>

		<dependency>
		    <groupId>jakarta.json</groupId>
		    <artifactId>jakarta.json-api</artifactId>
		</dependency>
	 -->
</dependencies>

1.3. 工具类

package io.os.es.kit;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;

public class ElasticClient {

	//同步需关闭传输对象:transport.close();
	private ElasticsearchClient syncClient;
	private ElasticsearchTransport transport;
	private ElasticsearchAsyncClient asyncClient;

	public ElasticsearchClient getSyncClient() {
		return syncClient;
	}
	public void setSyncClient(ElasticsearchClient syncClient) {
		this.syncClient = syncClient;
	}

	public ElasticsearchTransport getTransport() {
		return transport;
	}
	public void setTransport(ElasticsearchTransport transport) {
		this.transport = transport;
	}

	public ElasticsearchAsyncClient getAsyncClient() {
		return asyncClient;
	}
	public void setAsyncClient(ElasticsearchAsyncClient asyncClient) {
		this.asyncClient = asyncClient;
	}

	//syncClient操作完需关闭transport,而asyncClient则无需
	void connectToElastic(String userName,String cipherCode,String certificatePath,HttpHost httpHost) {
		try {
			final CredentialsProvider provider = new BasicCredentialsProvider();
			Credentials credential = new UsernamePasswordCredentials(userName,cipherCode);
			provider.setCredentials(AuthScope.ANY,credential);

			Path caPath = Path.of(certificatePath);
			CertificateFactory factory = CertificateFactory.getInstance("X.509");
			Certificate trustCa;
			try(InputStream is = Files.newInputStream(caPath)){
				trustCa = factory.generateCertificate(is);
			}

			KeyStore trustStore = KeyStore.getInstance("pkcs12");
			trustStore.load(null,null);
			trustStore.setCertificateEntry("ca",trustCa);

			SSLContextBuilder sslContextBuilder = SSLContexts.custom()
				.loadTrustMaterial(trustStore,null);
			final SSLContext sslContext = sslContextBuilder.build();

			RestClientBuilder builder = RestClient
				.builder(new HttpHost(httpHost.getHostName(),httpHost.getPort(),httpHost.getSchemeName()))
				.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
					@Override
					public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder asyncBuilder) {
						return asyncBuilder.setSSLContext(sslContext)
							.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
							.setDefaultCredentialsProvider(provider);
					}
				});
			RestClient restClient = builder.build();
			transport = new RestClientTransport(restClient,new JacksonJsonpMapper());

			syncClient = new ElasticsearchClient(transport);
			asyncClient = new ElasticsearchAsyncClient(transport);
		} catch (IOException | CertificateException | KeyStoreException
			| NoSuchAlgorithmException | KeyManagementException e) {
			e.printStackTrace();
		}
	}

}

1.4. 测试代码

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import org.apache.http.HttpHost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.transport.endpoints.BooleanResponse;

public class Tester {

	private static final Logger logger = LoggerFactory
		.getLogger(MethodHandles.lookup().lookupClass());

	public static void main(String[] sa) throws ElasticsearchException, IOException{
		ElasticClient client = new ElasticClient();

		String userName = "elastic";
		String cipherCode = "es8es8";
		String certificatePath = "src/main/resources/certificate/http_ca.crt";
		HttpHost httpHost = new HttpHost("192.168.0.123",9200,"https");

		client.connectToElastic(userName,cipherCode,certificatePath,httpHost);
		BooleanResponse response = client.getSyncClient().indices().exists(r -> r.index("elf"));
		logger.error("exist index:{}",response.value());
		client.getTransport().close();
	}

}
  • 21:15:17.754 [main] ERROR io.os.es.kit.Tester - exist index:true

  • org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties

2. Spring Data整合ES8

  • ElasticSearch 8.11.2

  • Spring Data 2023.1

  • Spring Data ElasticSearch 5.2.0

  • Spring Boot 3.2.0

2.1. Project Structure

📄 pom.xml
📂 src
  📂 main
    📂 java
      📂 io.os.es
        📄 ElasticEntry.java
        📂 config
          📄 ElasticConfig.java
        📂 controller
          📄 ElasticController.java
    📂 resources
      📄 application.yaml
      📂 certificate
        📄 http_ca.crt

2.2. Dependency

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

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

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-log4j2</artifactId>
	</dependency>
</dependencies>

<build>
	<resources>
		<resource>
	    	<directory>src/main/resources</directory>
	     	<filtering>true</filtering>
	     	<includes>
	        	<include>**/*.yaml</include>
	     	</includes>
		</resource>
	</resources>
</build>

2.3. application.yaml

spring:
  elasticsearch:
    username: elastic
    password: es8es8
    restclient:
      ssl:
        bundle: src/main/resources/certificate/http_ca.crt
    #此处不能携带schema,如https://192.168.0.123:9200
    uris:
    - 192.168.0.123:9200
  data:
    elasticsearch:
      repositories:
        enabled: true

2.4. Configuration

package io.os.es.config;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.time.Duration;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;

@Configuration
@EnableConfigurationProperties({ElasticsearchProperties.class})
public class ElasticConfig extends ElasticsearchConfiguration {

	@Autowired
	private ElasticsearchProperties prop;

    @Override
    public ClientConfiguration clientConfiguration() {
    	String[] uriArr = prop.getUris().toArray(String[]::new);
        return ClientConfiguration.builder()
            .connectedTo(uriArr)
            .usingSsl(getSslContext(),(hostname,session) -> true)
            .withConnectTimeout(Duration.ofSeconds(5))
            .withSocketTimeout(Duration.ofSeconds(30))
            .withBasicAuth(prop.getUsername(),prop.getPassword())
            .build();
    }

    private SSLContext getSslContext(){
		try {
			Path caPath = Path.of(prop.getRestclient().getSsl().getBundle());
			CertificateFactory factory = CertificateFactory.getInstance("X.509");
			Certificate trustCa;
			try(InputStream is = Files.newInputStream(caPath)){
				trustCa = factory.generateCertificate(is);
			}

			String keyStoreType = KeyStore.getDefaultType();
	        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
	        keyStore.load(null, null);
	        keyStore.setCertificateEntry("ca", trustCa);

	        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
	        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
	        tmf.init(keyStore);

	        SSLContext context = SSLContext.getInstance("TLS");
	        context.init(null,tmf.getTrustManagers(),null);
	        return context;
		} catch (CertificateException | IOException | KeyStoreException
			| NoSuchAlgorithmException | KeyManagementException e) {
			e.printStackTrace();
			return null;
		}
    }

}

2.5. Controller

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;

@RestController
@RequestMapping
public class ElasticController {

	private static final Logger logger = LoggerFactory
		.getLogger(MethodHandles.lookup().lookupClass());

	@Autowired
	private ElasticsearchClient esClient;

	@GetMapping("/create")
	public String createIndex() {
		try {
			String response = esClient.indices().create(r -> r.index(UUID.randomUUID().toString())).toString();
			logger.error("create index:{}",response);
			return response;
		} catch (ElasticsearchException | IOException e) {
			e.printStackTrace();
			return null;
		}
	}

}

2.6. Entry

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ElasticEntry {

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

}
2023-12-10T11:02:54.544+08:00 ERROR 40672 i.o.e.c.ElasticController
	create index:CreateIndexResponse:
{"index":"26726252-8571-416a-b47a-47a6dc2124b2","shards_acknowledged":true,"acknowledged":true}

3. Elastic Client

3.1. Imperative Client

@Configuration
public class ImperativeElasticConfig
	extends ElasticsearchConfiguration {}

@Autowired
ElasticsearchOperations imperativeOperation;

@Autowired
ElasticsearchClient imperativeClient;

@Autowired
RestClient restClient;

@Autowired
JsonpMapper jsonpMapper;

3.2. Reactive Client

@Configuration
public class ReactiveElasticConfig
	extends ReactiveElasticsearchConfiguration {}

@Autowired
ReactiveElasticsearchOperations reactiveOperation;

@Autowired
ReactiveElasticsearchClient reactiveClient;

@Autowired
RestClient restClient;

@Autowired
JsonpMapper jsonpMapper;

4. Api

  • ElasticsearchTemplate

  • ElasticsearchRepository

  • ElasticsearchOperations(Recommended)

    • IndexOperations

    • SearchOperations

    • DocumentOperations

5. Elastic Crud

5.1. Employee

@Document(indexName = "employee", createIndex = true)
public class Employee {

    @Id
    private String employeeId;

    @Field(type = FieldType.Text, name = "name")
    private String name;

    @Field(type = FieldType.Long, name = "salary")
    private long salary;

    public Employee() {}

	public String getEmployeeId() {
		return employeeId;
	}
	public void setEmployeeId(String employeeId) {
		this.employeeId = employeeId;
	}

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

	public long getSalary() {
		return salary;
	}
	public void setSalary(long salary) {
		this.salary = salary;
	}

}

5.2. Controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.os.elastic.model.Employee;

@RestController
@RequestMapping
public class EmployeeController {

	@Autowired
	private ElasticsearchOperations elasticOperation;

	@PostMapping("/employee")
	public ResponseEntity<Employee> insert(@RequestBody Employee employee){
		var emp = elasticOperation.save(employee);
		return ResponseEntity.ok(emp);
	}

	@DeleteMapping("/deleteById/{employeeId}")
	public ResponseEntity<String> deleteById(
			@PathVariable("employeeId") String employeeId){
		String empId = elasticOperation.delete(employeeId,Employee.class);
		return ResponseEntity.ok(empId);
	}

	@PutMapping("/updateById")
	public ResponseEntity<Employee> updateById(
		@RequestBody Employee employee){
		Employee emp = elasticOperation.get(employee.getEmployeeId(),Employee.class);
		emp.setName(employee.getName());
		emp.setSalary(employee.getSalary());
		elasticOperation.save(emp);
		return ResponseEntity.ok(emp);
	}

	@GetMapping("/getById/{employeeId}")
	public ResponseEntity<Employee> getById(
		@PathVariable("employeeId") String employeeId){
		Employee emp = elasticOperation.get(employeeId,Employee.class);
		return ResponseEntity.ok(emp);
	}

}

5.3. Test

  • localhost:8080/employee

{
    "name":"elf",
    "salary":"666"
}
  • localhost:8080/updateById

{
    "employeeId": "9KQfHI4BwaDO2KMCR8eQ",
    "name":"lml",
    "salary":"666"
}
  • localhost:8080/getById/86QaHI4BwaDO2KMCPcdA

  • localhost:8080/deleteById/8qQWHI4BwaDO2KMC7seg