首页 > 其他分享 >ElasticSearch全文搜索引擎 -Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文档)

ElasticSearch全文搜索引擎 -Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文档)

时间:2023-01-19 10:31:25浏览次数:42  
标签:http String url Spring org ElasticSearch 基本操作 import public


文章目录

  • ​​1. Spring Data概述​​
  • ​​2. Spring Data Elasticsearch​​
  • ​​3. 实现基本操作​​
  • ​​4. ElasticSearch操作文档​​
  • ​​5. ElasticSearch原生API操作工具类​​



1. Spring Data概述

Spring Data是spring提供的一套连接各种第三方数据源的框架集,它支持连接很多第三方数据源,例如:

  • 数据库
  • redis
  • ElasticSearch
  • MongoDB等

包括数据库在内,很多第三方数据都可以使用SpringData操作,非常方便。

ElasticSearch全文搜索引擎 -Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文档)_elasticsearch



2. Spring Data Elasticsearch

上面章节介绍了Spring Data可以连接很多第三方数据源,其中ES就是Spring Data可以连接的对象。原生情况下,我们需要使用socket来连接ES获得响应,再解析响应,代码量非常大,我们现在可以使用Spring Data提供的封装,连接ES,方便快捷。

转到knows-search模块:

  下面我们添加Spring Data ES的依赖:

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

application.properties:

# 搜索微服务端口
server.port=8066

# 搜索微服务名称
spring.application.name=search-service

# 定位ES的位置
spring.elasticsearch.rest.uris=http://localhost:9200

# 设置日志门槛,显示ES的操作信息
logging.level.cn.tedu.knows.search=debug
# 还需要进一步设置才能使输出日志更清晰
logging.level.org.elasticsearch.client.RestClient=debug

SpringBoot启动类无需配置!



3. 实现基本操作

操作ES需要类:首先定义一个对应ES数据的类型,创建一个vo包,包中定义Item(商品)类代码如下:

package cn.tedu.knows.search.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Data // lombok
@Accessors(chain = true) // 链式赋值(连续set方法)
@AllArgsConstructor // 全参构造
@NoArgsConstructor // 无参构造
//指定当前类对象对应哪个ES中的索引
//如果索引不存在,会自动创建
@Document(indexName = "items")
public class Item {
// 表示当前ES索引的id列
@Id
private Long id;
// 需要分词的属性使用Text类型,并指定分词器
@Field(type = FieldType.Text,analyzer = "ik_smart",
searchAnalyzer = "ik_smart")
private String title; //商品名称
// 不需要分词的属性使用Keyword类型,不用写分词器
@Field(type = FieldType.Keyword)
private String category;//分类

@Field(type = FieldType.Keyword)
private String brand; //品牌

@Field(type = FieldType.Double)
private Double price; //价格

//不会使用图片地址查询,设置index = false表示当前属性不会创建索引,节省空间,因为这个属性不会被查询
@Field(type = FieldType.Keyword,index = false)
private String images; //图片地址
// /upload/2021/08/19/abc.jpg

}

这个类中所有属性均配置了对应ES的属性和类型,下面我们就可以使用这个类操作ES了。

创建一个包repository,创建一个接口ItemRepository:

@Repository //将实现类的对象存到Spring容器中
//ElasticsearchRepository实现基本的增删改查
public interface ItemRepository extends ElasticsearchRepository<Item,Long> {

}

这个接口和Mybatis Plus中Mapper接口继承的BaseMapper类似,会自动提供基本的增删改查方法。下面进行测试,测试类代码如下:

package cn.tedu.knows.search;

import cn.tedu.knows.search.repository.ItemRepository;
import cn.tedu.knows.search.vo.Item;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@SpringBootTest
class KnowsSearchApplicationTests {
@Resource
ItemRepository itemRepository;
@Test
void contextLoads() {
//实例化对象,新增一个商品
Item item = new Item()
.setId(1L)
.setTitle("罗技激光无线游戏鼠标")
.setCategory("鼠标")
.setBrand("罗技")
.setImages("/1.jpg")
.setPrice(285.0);
//调用Spring Data提供的方法进行新增,save表示新增
itemRepository.save(item);
System.out.println("ok");
}

//按id查询
@Test
public void getId(){
//Optional表示装着Item的盒子
Optional<Item> optional = itemRepository.findById(1L);
//通过get进行查询
System.out.println(optional.get());
}

//批量新增
@Test
public void addAll(){
List<Item> list = new ArrayList<>();
list.add(new Item(2L,"罗技机械无线游戏键盘","键盘","罗技",360.0,"/2.jpg"));
list.add(new Item(3L,"雷蛇激光有线游戏鼠标","鼠标","雷蛇",488.0,"/3.jpg"));
list.add(new Item(4L,"罗技降噪蓝牙竞技耳机","耳机","罗技",378.0,"/4.jpg"));
list.add(new Item(5L,"华为静音办公有线鼠标","鼠标","华为",220.0,"/5.jpg"));
list.add(new Item(6L,"雷蛇竞技机械无线键盘","键盘","雷蛇",425.0,"/6.jpg"));
itemRepository.saveAll(list);
System.out.println("ok");
}

//全查
@Test
public void getAll(){
//Iterable是List的父接口
Iterable<Item> list = itemRepository.findAll();
for(Item item : list){
System.out.println(item);
}
}

}
//上面都是基本操作,不需要我们自己写接口中的方法

上面进行了单增、单查、批量增和全查的操作,下面进行自定义的查询。

Spring Data支持编写方法名表达操作,会自动按方法名的表达生成实现代码,这是它的一大优势!

在ItemRepository接口编写方法:

// Spring Data框架连接数据源,可以通过方法名来表达操作含义
// 根据商品的title属性执行模糊查询
Iterable<Item> queryItemsByTitleMatches(String title);

测试代码

// 下面要完成一些条件查询,需要调用ItemRepository接口中编写的方法
// 商品标题模糊匹配
@Test
public void queryByTitle(){
Iterable<Item> items=itemRepository.queryItemsByTitleMatches("无线");
for(Item item: items){
System.out.println(item);
}
}

相当于运行了下面的指令

### 单条件搜索
POST http://localhost:9200/items/_search
Content-Type: application/json

{
"query": {"match": { "title": "无线" }}
}

多属性条件查询:在ItemRepository接口编写方法:

// 根据商品的title和brand执行模糊查询
Iterable<Item> queryItemsByTitleMatchesAndBrandMatches(String title,String brand);

测试类:

// 测试多条件查询
@Test
public void queryByTitleBrand(){
Iterable<Item> items=itemRepository
.queryItemsByTitleMatchesAndBrandMatches(
"游戏","罗技"
);
for(Item item: items){
System.out.println(item);
}
}

实际执行的请求:

### 多字段搜索
POST http://localhost:9200/items/_search
Content-Type: application/json

{
"query": {
"bool": {
"must": [
{ "match": { "title": "游戏" }},
{ "match": { "brand": "罗技"}}
]
}
}
}

排序查询:在ItemRepository接口编写方法:

// 排序查询:按照价格降序查询标题或者品牌匹配的商品
Iterable<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(String title,String brand);

测试代码:

// 测试排序
@Test
public void order(){
Iterable<Item> items = itemRepository.queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc("游戏","罗技");
for(Item item:items){
System.out.println(item);
}
}

实际运行的请求:

### 多字段搜索

POST http://localhost:9200/items/_search
Content-Type: application/json

{
"query": {
"bool": {
"should": [
{ "match": { "title": "游戏" }},
{ "match": { "brand": "罗技"}}
]
}
},"sort":[{"price":"desc"}]
}

添加分页查询功能::在ItemRepository接口编写方法

// 分页查询 Page相当于PageHelper,Pageable规定第几页包含多少行,相当于PageInfo
Page<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(String title, String brand, Pageable pageable);

测试:

//分页查询
@Test
public void page(){
int pageNum=1;
int pageSize=2;
//PageRequest.of返回Pageable,Pageable页数从第0页开始
Page<Item> page=itemRepository.queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(
"游戏","罗技",PageRequest.of(pageNum-1,pageSize));
//page实现了Iterable接口
for(Item item:page){
System.out.println(item);
}
}

根据条件分页查询

//Repository接口定义
public interface BookInfoEsMapper extends ElasticsearchRepository<BookInfoEs, String> {
Page<BookInfoEs> findBookInfoEsByTitleOrIsbnOrAuthor(String title, String isbn, String author, Pageable pageable);
}
@Autowired
private BookInfoEsMapper bookInfoEsMapper;

@Test
public void test() {
//调用
// Repository层根据findBookInfoEsByTitleOrIsbnOrAuthor 方法名自动识别条件
Pageable pageable = PageRequest.of(0,10);
Page<BookInfoEs> list = bookInfoEsMapper.findBookInfoEsByTitleOrIsbnOrAuthor("现代", "", "", pageable);
System.out.println(list);
System.out.println("总条数:" +list.getTotalElements());
System.out.println("总页数:" +list.getTotalPages());
}

再来一种 根据条件分页查询

@Autowired
private ElasticsearchOperations elasticsearchOperations;

@Test
public void test() {
//构造条件
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//must:必须满足的条件
//should:非必须满足的条件
boolQueryBuilder.should(QueryBuilders.matchQuery("title", "现代"));

//wildcardQuery: 通配符查询
boolQueryBuilder.should(QueryBuilders.wildcardQuery("id", "*122*"));
boolQueryBuilder.should(QueryBuilders.matchQuery("author", ""));

Pageable pageable = PageRequest.of(0, 10);

NativeSearchQuery build = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withPageable(pageable)
.build();

SearchHits<BookInfoEs> search = elasticsearchOperations.search(build, BookInfoEs.class);
System.out.println("检索后的总分页数目为:" + search.getTotalHits());
List<SearchHit<BookInfoEs>> searchHits = search.getSearchHits();
System.out.println(searchHits);
}

基于​​RestHighLevelClient​​批量操作

import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.rest.RestStatus;

@Autowired
private RestHighLevelClient client;

BulkRequest bulkRequest = new BulkRequest();
for(int i = 0; i < 100; i++){
//添加、删除、更新文档,操作索引都有相关类就不一一举例了
UpdateRequest updateRequest = new UpdateRequest(indexName, IndexConst.DEFAULT_TYPE_NAME, docId);
updateRequest.doc(docObject.toJSONString(), XContentType.JSON);
bulkRequest.add(updateRequest);
}
if (!bulkRequest.requests().isEmpty()) {
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
BulkItemResponse[] items = bulkResponse.getItems();

for (BulkItemResponse item : items) {
if (item.status() != RestStatus.OK) {
log.error(JSONObject.toJSONString(item));
}
}
}



4. ElasticSearch操作文档

### 创建 index
PUT http://localhost:9200/questions
### 删除一个Index
DELETE http://localhost:9200/questions
### 设置index中的文档属性采用ik分词
### type=text的才能分词,analyzer表示分词器,根据分词器对text内容进行分词,建立索引
### search_analyzer表示搜索内容的分词器,一般与上面的分词器相同,建立索引
### _mapping配合properties用来设置属性
### 注意下面的换行,这里是回车并换行,有严格格式要求,必须这样书写
POST http://localhost:9200/questions/_mapping
Content-Type: application/json

{
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
### questions中添加文档
### POST一般为新增或修改的意思,_create表示创建文档,/1中的1表示文档id,为真正的id
### 每执行一次请求必须通过###来分割,既是分隔符,也是注释符
POST http://localhost:9200/questions/_create/1
Content-Type: application/json

{
"id":1,
"title":"Java基本数据类型有哪些",
"content":"面时候为啥要问基本类型这么简单问题呀,我们要如何回答呢?"
}

### questions 中添加文档
POST http://localhost:9200/questions/_create/2
Content-Type: application/json

{
"id":2,
"title":"int类型的范围",
"content":"为啥要了解int类型的范围呢?"
}

### questions 中添加文档
POST http://localhost:9200/questions/_create/3
Content-Type: application/json

{
"id":3,
"title":"常用集合类有哪些",
"content":"为啥企业经常问集合呀?该如何回复呢"
}

### questions 中添加文档
POST http://localhost:9200/questions/_create/4
Content-Type: application/json

{
"id":4,
"title":"线程的run方法和start方法有啥区别",
"content":"run方法可以执行线程的计算过程, start也可以执行线程的计算过程,用途一样么?"
}
### 更新questions索引中的文档
### 此处POST是更新的意思,表示对文档4进行更新
POST http://localhost:9200/questions/_doc/4/_update
Content-Type: application/json

{
"doc": {
"title": "Java线程的run方法和start方法有啥区别"
}
}
### 删除questions中的一个文档,DELETE表示删除
DELETE http://localhost:9200/questions/_doc/2
### 查询数据,GET表示查询
GET http://localhost:9200/questions/_doc/4
### 分词搜索 单属性模糊查询 查询分词索引,按照输出得分(_score:查询内容占整个内容的比例)由高到低排序
POST http://localhost:9200/questions/_search
Content-Type: application/json

{
"query": { "match": {"title": "类型" } }
}
### 多字段搜索 多属性模糊查询 格式固定
### bool表示真假,should表示或,must表示与
### 查询的内容也会分词
POST http://localhost:9200/questions/_search
Content-Type: application/json

{
"query": {
"bool": {
"should": [
{ "match": { "title": "java类型" }},
{ "match": { "content": "java类型"}}
]
}
}
}

5. ElasticSearch原生API操作工具类

最后附上自己写的一个请求工具类(使用这个不需要引入spring-data-es的jar包了,是依靠es自带的http请求操作)

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.util.Base64;

/**
* @author: yh
* @Description: restTemplate工具类
* @date: 2022/8/4 15:43
*/
@Component
public class RestTemplateUtil {
@Value("${spring.elasticsearch.rest.uris}")
private String host;

@Value("${spring.elasticsearch.rest.username}")
private String userName;

@Value("${spring.elasticsearch.rest.password}")
private String passWord;

/**
* 认证的Authorization
*/
private static String authentication;
/**
* 查询ES POST-url
*/
public static String SEARCH_INDEX_URL = null;
/**
* 多个搜索API-url
*/
public static String MSEARCH_INDEX_URL = null;

/**
* 查询文档-url
*/
public static String GET_DOC_ID = null;

/**
* 新建文档-url
*/
public static String CREATE_DOC = null;

/**
* 更新文档 POST-url
*/
public static String UPDATE_INDEX_DOC = null;

/**
* 按查询更新API-url
*/
public static String UPDATE_BY_QUERY = null;

/**
* 创建ES索引,PUT请求-url
*/
public static String CREATE_INDEX = null;

/**
* 批量增删改API POST-url
*/
public static String BULK_URL = null;

/**
* 根据文档id查询文档,也可以判断文档是否存在-url
*/
public static String QUERY_INDEX_DOC = null;

/**
* Multi get (mget) API-url
*/
public static String INDEX_MGET = null;

/**
* 删除索引(危险) delete请求
*/
public static String DELETE_INDEX = null;

@PostConstruct
public void initProperty() {
//{esIndex}:操作索引名称,{id}:操作文档id
SEARCH_INDEX_URL = "http://" + host + "/{esIndex}/_search";
MSEARCH_INDEX_URL = "http://" + host + "/{esIndex}/_msearch";
GET_DOC_ID = "http://" + host + "/{esIndex}/_doc/{id}";
CREATE_DOC = "http://" + host + "/{esIndex}/_create/{id}";
UPDATE_INDEX_DOC = "http://" + host + "/{esIndex}/_doc/{id}/_update";
UPDATE_BY_QUERY = "http://" + host + "/{esIndex}/_update_by_query";
CREATE_INDEX = "http://" + host + "/{esIndex}";
BULK_URL = "http://" + host + "/{esIndex}/_bulk";
QUERY_INDEX_DOC = "http://" + host + "/{esIndex}/_doc/{id}";
INDEX_MGET = "http://" + host + "/{esIndex}/_mget";
DELETE_INDEX = "http://" + host + "/{esIndex}";

authentication = "Basic " + Base64.getEncoder().encodeToString((userName + ":" + passWord).getBytes());
}

private static RestTemplate restTemplate;

@Autowired
public void setRestTemplate(RestTemplate restTemplate) {
RestTemplateUtil.restTemplate = restTemplate;
}

/**
* POST请求
*
* @param url 请求路径
* @param jsonStr 参数
* @return String
* @author yh
* @date 2022/8/4
*/
public static String postForObject(String url, String jsonStr) {
//设置header信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
requestHeaders.set("authorization", authentication);

HttpEntity<String> requestEntity = new HttpEntity<>(jsonStr, requestHeaders);

return restTemplate.postForObject(url, requestEntity, String.class);
}

/**
* ndjson格式数据请求
*
* @param ndjson 一种数据格式
* @param url url
* @return String
* @author yh
* @date 2022/8/5
*/
public static String postForNdjson(String url, String ndjson) {
if (StringUtils.isBlank(ndjson)) {
return "{}";
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/x-ndjson");
headers.set("authorization", authentication);
HttpEntity<String> request = new HttpEntity<>(ndjson, headers);

return restTemplate.postForObject(url, request, String.class);
}

/**
* get查询es
*
* @param url 请求路径
* @return String
* @author yh
* @date 2022/8/5
*/
public static String getForString(String url) {
if (StringUtils.isBlank(url)) {
return "{}";
}
HttpHeaders headers = new HttpHeaders();
headers.set("authorization", authentication);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity(null, headers);

return restTemplate.exchange(url, HttpMethod.GET, request, String.class).getBody();
// return restTemplate.getForObject(url, String.class);
}

/**
* PUT请求
*
* @param url 请求路径
* @param jsonStr body参数
* @author yh
* @date 2022/8/5
*/
public static void putForVoid(String url, String jsonStr) {
//设置header信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
requestHeaders.set("authorization", authentication);

HttpEntity<String> requestEntity = new HttpEntity<>(jsonStr, requestHeaders);
restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class);
// restTemplate.put(url, JSONObject.parse(json));
}

/**
* delete请求
*
* @param url 请求路径
* @author yh
* @date 2022/8/11
*/
public static void deleteForVoid(String url) {
restTemplate.delete(url);
}
}

需要注意的是​​_bulk​​批量操作时,换行符的使用(​​_bulk操作ES​​)

/**
* 不同系统的换行符
* @date 2022/8/11
*/
private String newLine = System.getProperty("line.separator");

还有一点注意的是:当操作es索引时,索引不存在就会返回​​404​​​,不做配置的话会让程序直接抛出异常终止运行,我们希望状态码返回​​404​​​时,走创建索引的逻辑,这时候就需要把​​RestTemplate​​相关的状态码加入白名单

import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;

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

/**
* 功能:捕获RestTemplate异常
*
* @author yh
* @date 2022/8/10
*/
public class RtErrorHandler extends DefaultResponseErrorHandler {

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return super.hasError(response);
}

@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
// 白名单。白名单上的异常则不处理,直接返回
List<HttpStatus> donotDeal = new ArrayList<>();
// 404不要抛异常
donotDeal.add(HttpStatus.NOT_FOUND);
// donotDeal.add(HttpStatus.BAD_REQUEST);

// 非白名单则处理
if (!donotDeal.contains(statusCode)) {
super.handleError(response);
}
}
}
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
* @author: yh
* @Description:
* @date: 2022/8/5 10:32
*/
@Configuration
public class RestConfig {

/**
* 定义restTemplate请求处理
* @param builder 可用于配置和创建RestTemplate的生成器。提供方便的方法来注册转换器、错误处理程序和UriTemplateHandlers。
* @author yh
* @date 2022/8/10
* @return RestTemplate
*/
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
RestTemplate build = builder.build();
build.setErrorHandler(new RtErrorHandler());
return build;
}
}

标签:http,String,url,Spring,org,ElasticSearch,基本操作,import,public
From: https://blog.51cto.com/u_14452299/6019894

相关文章

  • Elasticsearch笔记(集群插件、kibana、什么是倒排索引)
    Elasticsearch中的集群、节点、索引、文档、类型集群是一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索引和搜索功能。群集由唯一名称标识,......
  • Spring Batch多步骤任务、并行执行、任务决策器、任务嵌套
    道虽迩(ěr),不行不至,事虽小,不为不成......
  • Spring-webflux 响应式编程
    热爱可抵漫长岁月文章目录​​1.前言​​​​2.Spring-webflux简介​​​​3.什么是“响应式”​​​​4.Spring-webflux的响应式API​​​​5.SpringMVC还是WebFlu......
  • Spring自定义启动图标(个性化启动图标)
     今天比昨天好,这就是希望。☀️☀️☀️ 文章目录​​个性化输出​​​​图片转换输出​​​​生成banner的网站​​Spring默认启动输出.________......
  • SpringCloud Tencent Polaris
    北极星是腾讯开源的服务发现和治理中心,致力于解决分布式或者微服务架构中的服务可见、故障容错、流量控制和安全问题。虽然,业界已经有些组件可以解决其中一部分问题,但是缺少......
  • SpringBoot+MyBatils-puls+db2
    记录自己搭建db2数据库,链接数据库遇到的问题。 搭建db2我是在阿里云使用docker搭建的db2数据库,搭建数据库参考下面这个链接https://www.cnblogs.com/Yongzhouunknown/p......
  • elasticsearch之search template
    一、searchtemplate简介elasticsearch提供了searchtemplate功能,其会在实际执行查询之前,对searchtemplate进行预处理并将参数填充到template中。elasticsearch主要提供......
  • 14.spring声明事务
    1.要开启Spring的事务处理功能,在Spring的配置文件中创建一个DataSourceTransactionManager 对象:<!--配置声明式事务--><beanid="transationManager"class="org.s......
  • Spring JAXB
    JAXB是 用于XML绑定的Java体系结构的首字母缩写。它允许Java开发人员将Java类映射为XML表示形式。JAXB可用于将Java对象编组为XML,反之亦然。它是Sun提供的OXM(对象XML映......
  • Spring和JMS集成
    要将spring与JMS集成,您需要创建两个应用程序。JMS接收器应用程序JMSSender应用程序要使用spring创建JMS应用程序,我们使用Apache的 ActiveMQServer 创建队列。让我......