ProductController.java
package com. example. controller ;
import com. example. service. IProductService ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. web. bind. annotation. GetMapping ;
import org. springframework. web. bind. annotation. PathVariable ;
import org. springframework. web. bind. annotation. RequestMapping ;
import org. springframework. stereotype. Controller ;
import org. springframework. web. bind. annotation. RestController ;
import java. util. Map ;
@RestController
@RequestMapping ( "product" )
public class ProductController {
@Autowired
private IProductService productService;
@GetMapping ( "saveProductToES" )
public String saveProductToES ( ) {
productService. saveProductFromDBToES ( ) ;
return "ok" ;
}
@GetMapping ( "delete/{productId}" )
public String deleteProduct ( @PathVariable Integer productId) {
return productService. deleteProduct ( productId) ;
}
@GetMapping ( "kw/{kw}/page/{pageNum}" )
public Map < String , Object > getByKeyword ( @PathVariable ( "kw" ) String keyword,
@PathVariable ( "pageNum" ) Integer pageNum) {
if ( pageNum == null )
pageNum = 1 ;
Map < String , Object > result = productService. getByNameAndInfo ( keyword, keyword, pageNum) ;
return result;
}
}
Product.java
package com. example. entity ;
import com. baomidou. mybatisplus. annotation. IdType ;
import com. baomidou. mybatisplus. annotation. TableId ;
import org. springframework. data. annotation. Id ;
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 java. io. Serializable ;
import java. math. BigDecimal ;
import java. time. LocalDateTime ;
@Document ( indexName = "myproduct" )
public class Product implements Serializable {
private static final long serialVersionUID = 1L ;
@TableId ( value = "product_id" , type = IdType . AUTO )
@Id
private Integer productId;
@Field ( type = FieldType. Keyword )
private String productName;
private BigDecimal productPrice;
private String productImg;
private Integer productCount;
@Field ( type = FieldType. Date , name = "update_time" , format = { } ,
pattern = "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00' || strict_date_opotional_time || epoch_millis" )
private LocalDateTime createTime;
@Field ( type = FieldType. Date , name = "update_time" , format = { } ,
pattern = "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00' || strict_date_opotional_time || epoch_millis" )
private LocalDateTime updateTime;
@Field ( type = FieldType. Text , analyzer = "ik_smart" , searchAnalyzer = "ik_max_word" )
private String productInfo;
public Integer getProductId ( ) {
return productId;
}
public void setProductId ( Integer productId) {
this . productId = productId;
}
public String getProductName ( ) {
return productName;
}
public void setProductName ( String productName) {
this . productName = productName;
}
public BigDecimal getProductPrice ( ) {
return productPrice;
}
public void setProductPrice ( BigDecimal productPrice) {
this . productPrice = productPrice;
}
public String getProductImg ( ) {
return productImg;
}
public void setProductImg ( String productImg) {
this . productImg = productImg;
}
public Integer getProductCount ( ) {
return productCount;
}
public void setProductCount ( Integer productCount) {
this . productCount = productCount;
}
public LocalDateTime getCreateTime ( ) {
return createTime;
}
public void setCreateTime ( LocalDateTime createTime) {
this . createTime = createTime;
}
public LocalDateTime getUpdateTime ( ) {
return updateTime;
}
public void setUpdateTime ( LocalDateTime updateTime) {
this . updateTime = updateTime;
}
public String getProductInfo ( ) {
return productInfo;
}
public void setProductInfo ( String productInfo) {
this . productInfo = productInfo;
}
@Override
public String toString ( ) {
return "Product{" +
"productId=" + productId +
", productName=" + productName +
", productPrice=" + productPrice +
", productImg=" + productImg +
", productCount=" + productCount +
", createTime=" + createTime +
", updateTime=" + updateTime +
", productInfo=" + productInfo +
"}" ;
}
}
ElasticsearchSyncListener.java
package com. example. listener ;
import com. example. service. impl. ProductDeletedEvent ;
import com. example. mapper. ProductElasticSearchMapper ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. context. event. EventListener ;
import org. springframework. stereotype. Component ;
@Component
public class ElasticsearchSyncListener {
@Autowired
private ProductElasticSearchMapper productElasticSearchMapper;
@EventListener
public void handleProductDeletedEvent ( ProductDeletedEvent event) {
Integer productId = event. getProductId ( ) ;
productElasticSearchMapper. deleteById ( productId) ;
}
}
ProductElasticSearchMapper.java
package com. example. mapper ;
import com. example. entity. Product ;
import org. springframework. data. elasticsearch. repository. ElasticsearchRepository ;
import org. springframework. stereotype. Repository ;
import java. util. List ;
@Repository
public interface ProductElasticSearchMapper extends ElasticsearchRepository < Product , Integer > {
public List < Product > findByProductName ( String productName) ;
public List < Product > findByProductInfo ( String productInfo) ;
}
ProductMapper.java
package com. example. mapper ;
import com. example. entity. Product ;
import com. baomidou. mybatisplus. core. mapper. BaseMapper ;
import org. springframework. data. elasticsearch. repository. ElasticsearchRepository ;
import org. springframework. stereotype. Repository ;
import java. util. List ;
public interface ProductMapper extends BaseMapper < Product > {
}
ProductDeletedEvent.java
package com. example. service. impl ;
import org. springframework. context. ApplicationEvent ;
public class ProductDeletedEvent extends ApplicationEvent {
private final Integer productId;
public ProductDeletedEvent ( Object source, Integer productId) {
super ( source) ;
this . productId = productId;
}
public Integer getProductId ( ) {
return productId;
}
}
ProductServiceImpl.java
package com. example. service. impl ;
import com. example. entity. Product ;
import com. example. mapper. ProductElasticSearchMapper ;
import com. example. mapper. ProductMapper ;
import com. example. service. IProductService ;
import org. elasticsearch. index. query. BoolQueryBuilder ;
import org. elasticsearch. index. query. MatchQueryBuilder ;
import org. elasticsearch. index. query. QueryBuilder ;
import org. elasticsearch. index. query. QueryBuilders ;
import org. elasticsearch. search. fetch. subphase. highlight. HighlightBuilder ;
import org. elasticsearch. search. sort. FieldSortBuilder ;
import org. elasticsearch. search. sort. SortBuilder ;
import org. elasticsearch. search. sort. SortBuilders ;
import org. elasticsearch. search. sort. SortOrder ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. context. ApplicationEventPublisher ;
import org. springframework. context. event. EventListener ;
import org. springframework. data. domain. PageRequest ;
import org. springframework. data. elasticsearch. core. * ;
import org. springframework. data. elasticsearch. core. query. NativeSearchQuery ;
import org. springframework. data. elasticsearch. core. query. NativeSearchQueryBuilder ;
import org. springframework. stereotype. Service ;
import java. util. ArrayList ;
import java. util. HashMap ;
import java. util. List ;
import java. util. Map ;
@Service
public class ProductServiceImpl implements IProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private ProductElasticSearchMapper productElasticSearchMapper;
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public boolean saveProductFromDBToES ( ) {
List < Product > productList = productMapper. selectList ( null ) ;
Iterable < Product > products = productElasticSearchMapper. saveAll ( productList) ;
return true ;
}
@Override
public String deleteProduct ( Integer productId) {
int rows = productMapper. deleteById ( productId) ;
if ( rows> 0 ) {
applicationEventPublisher. publishEvent ( new ProductDeletedEvent ( this , productId) ) ;
return "删除成功" ;
}
return "删除失败" ;
}
@Override
public Map < String , Object > getByNameAndInfo ( String productName, String productInfo, Integer pageNum) {
PageRequest page = PageRequest . of ( pageNum - 1 , 3 ) ;
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder ( ) ;
if ( productName != null ) {
QueryBuilder queryBuilder = QueryBuilders . queryStringQuery ( productName) ;
boolQueryBuilder. must ( queryBuilder) ;
} else {
if ( productInfo != null )
boolQueryBuilder. must ( new MatchQueryBuilder ( "productInfo" , productInfo) ) ;
}
SortBuilder sortBuilder = SortBuilders . fieldSort ( "productPrice" ) . order ( SortOrder . DESC ) ;
NativeSearchQueryBuilder builder= new NativeSearchQueryBuilder ( ) ;
NativeSearchQuery query= builder
. withQuery ( boolQueryBuilder)
. withPageable ( page)
. withSort ( sortBuilder)
. withHighlightFields (
new HighlightBuilder. Field ( "productInfo" ) ,
new HighlightBuilder. Field ( "productName" ) )
. withHighlightBuilder ( new HighlightBuilder ( )
. preTags ( "<span style='color:red'>" )
. postTags ( "</span>" ) )
. build ( ) ;
SearchHits < Product > search = restTemplate. search ( query, Product . class ) ;
List < Product > productList= new ArrayList < > ( ) ;
for ( SearchHit < Product > searchHit: search) {
Map < String , List < String > > highlightFields = searchHit. getHighlightFields ( ) ;
String highLightProName = highlightFields. get ( "productName" ) == null ? searchHit. getContent ( ) . getProductName ( ) : highlightFields. get ( "productName" ) . get ( 0 ) ;
String highLightProInfo = highlightFields. get ( "productInfo" ) == null ? searchHit. getContent ( ) . getProductInfo ( ) : highlightFields. get ( "productInfo" ) . get ( 0 ) ;
searchHit. getContent ( ) . setProductName ( highLightProName ) ;
searchHit. getContent ( ) . setProductInfo ( highLightProInfo) ;
productList. add ( searchHit. getContent ( ) ) ;
}
SearchPage < Product > searchPage= SearchHitSupport . searchPageFor ( search, query. getPageable ( ) ) ;
long totalElements= searchPage. getTotalElements ( ) ;
int totalPages= searchPage. getTotalPages ( ) ;
int currentPageForDisplay= searchPage. getPageable ( ) . getPageNumber ( ) + 1 ;
System . out. println ( currentPageForDisplay) ;
Map < String , Object > map= new HashMap < > ( ) ;
map. put ( "totalElements" , totalElements) ;
map. put ( "totalPages" , totalPages) ;
map. put ( "currentPage" , currentPageForDisplay) ;
map. put ( "productList" , productList) ;
return map;
}
}
SyncProductService.java
package com. example. service. impl ;
import com. example. service. IProductService ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. scheduling. annotation. Scheduled ;
import org. springframework. stereotype. Service ;
@Service
public class SyncProductService {
@Autowired
private IProductService productService;
@Scheduled ( fixedRate = 2000 )
public void syncProductsFromDBToES ( ) {
productService. saveProductFromDBToES ( ) ;
}
}
IProductService.java
package com. example. service ;
import com. example. entity. Product ;
import com. baomidou. mybatisplus. extension. service. IService ;
import java. util. Map ;
public interface IProductService {
public boolean saveProductFromDBToES ( ) ;
public String deleteProduct ( Integer productId) ;
public Map < String , Object > getByNameAndInfo ( String productName, String productInfo, Integer pageNum) ;
}
ElasticSearchSpringDemoApplication.java
package com. example ;
import org. mybatis. spring. annotation. MapperScan ;
import org. springframework. boot. SpringApplication ;
import org. springframework. boot. autoconfigure. SpringBootApplication ;
import org. springframework. scheduling. annotation. EnableScheduling ;
@SpringBootApplication
@MapperScan ( "com.example.mapper" )
@EnableScheduling
public class ElasticSearchSpringDemoApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( ElasticSearchSpringDemoApplication . class , args) ;
}
}
ServletInitializer.java
package com. example ;
import org. springframework. boot. builder. SpringApplicationBuilder ;
import org. springframework. boot. web. servlet. support. SpringBootServletInitializer ;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure ( SpringApplicationBuilder application) {
return application. sources ( ElasticSearchSpringDemoApplication . class ) ;
}
}
product.sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0 ;
DROP TABLE IF EXISTS ` product` ;
CREATE TABLE ` product` (
` product_id` int ( 0 ) NOT NULL AUTO_INCREMENT ,
` product_name` varchar ( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL ,
` product_price` decimal ( 10 , 2 ) NULL DEFAULT NULL ,
` product_img` varchar ( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL ,
` product_count` int ( 0 ) NULL DEFAULT NULL ,
` create_time` datetime ( 0 ) NULL DEFAULT NULL ,
` update_time` datetime ( 0 ) NULL DEFAULT NULL ,
` product_info` varchar ( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL ,
PRIMARY KEY ( ` product_id` ) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1 ;
同步
增量同步:设计一个机制来跟踪数据库中的更改(如使用数据库的binlog日志),并仅同步自上次同步以来发生的更改。这通常比较复杂,但可以实现实时或近实时的数据同步。
定期全量同步:可以定期(如每小时、每天)运行saveProductFromDBToES方法来进行全量同步。这种方法比较简单,但可能会导致数据在一定时间窗口内不同步。
删除操作同步:在应用程序中添加逻辑,以便在数据库记录被删除时,也在Elasticsearch 中删除相应的文档。这通常需要在数据库删除操作的地方添加额外的代码或使用触发器。
使用监听器或事件驱动:如果使用的是支持事件驱动或变更数据捕获(CDC )的数据库或框架,可以配置监听器来捕获数据库更改事件,并据此更新Elasticsearch 中的数据。
ProductMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<! DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace = " com.example.mapper.ProductMapper" >
</ mapper>
application.yaml
server :
servlet :
context-path : /es_demo
spring :
datasource :
driver-class-name : com.mysql.cj.jdbc.Driver
url : jdbc: mysql: //localhost: 3306/cloud_product_db? useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username : root
password : 123456
elasticsearch :
uris : localhost: 9200
connection-timeout : 5s
socket-timeout : 30s
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
< project xmlns = " http://maven.apache.org/POM/4.0.0" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance"
xsi: schemaLocation= " http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" >
< modelVersion> 4.0.0</ modelVersion>
< parent>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-parent</ artifactId>
< version> 2.7.6</ version>
< relativePath/>
</ parent>
< groupId> com.example</ groupId>
< artifactId> elasticSearchSpringDemo</ artifactId>
< version> 0.0.1-SNAPSHOT</ version>
< packaging> war</ packaging>
< name> elasticSearchSpringDemo</ name>
< description> elasticSearchSpringDemo</ description>
< properties>
< java.version> 1.8</ java.version>
</ properties>
< dependencies>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 8.0.28</ version>
</ dependency>
< dependency>
< groupId> com.baomidou</ groupId>
< artifactId> mybatis-plus-boot-starter</ artifactId>
< version> 3.5.2</ version>
</ dependency>
< dependency>
< groupId> com.baomidou</ groupId>
< artifactId> mybatis-plus-generator</ artifactId>
< version> 3.5.1</ version>
</ dependency>
< dependency>
< groupId> org.freemarker</ groupId>
< artifactId> freemarker</ artifactId>
< version> 2.3.31</ version>
</ dependency>
< dependency>
< groupId> org.elasticsearch.client</ groupId>
< artifactId> elasticsearch-rest-high-level-client</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-data-elasticsearch</ artifactId>
</ dependency>
< dependency>
< groupId> org.apache.lucene</ groupId>
< artifactId> lucene-core</ artifactId>
< version> 8.11.1</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-tomcat</ artifactId>
< scope> provided</ scope>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-test</ artifactId>
< scope> test</ scope>
</ dependency>
</ dependencies>
< build>
< plugins>
< plugin>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-maven-plugin</ artifactId>
</ plugin>
</ plugins>
</ build>
</ project>