Spring Boot2集成Elasticsearch、PostgreSQL遇到的问题

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 项目背景  在描述和还原事故之前,简单说明下相关环境:spring boot v2.0.4.RELEASEspring-boot-starter-data-elasticsearch (以前做项目的时候,Spring Data ES跟ES服务存在版本匹配关系,但目前在spring boot v2.

项目背景

  在描述和还原事故之前,简单说明下相关环境:

  • spring boot v2.0.4.RELEASE
  • spring-boot-starter-data-elasticsearch (以前做项目的时候,Spring Data ES跟ES服务存在版本匹配关系,但目前在spring boot v2.0.4.RELEASE中使用未发现有版本不兼容情况)
  • spring-boot-starter-data-jpa (用于操作PostgreSQL)

PostgreSQL启动连接报错

  启动项目的时候出现错误,具体异常信息如下:

2018-08-29 21:33:18,397 INFO  org.hibernate.dialect.Dialect[157]: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL95Dialect
2018-08-29 21:33:21,479 INFO  o.h.e.j.e.i.LobCreatorBuilderImpl[124]: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
java.lang.reflect.InvocationTargetException: null
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl.useContextualLobCreation(LobCreatorBuilderImpl.java:113)
    at org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl.makeLobCreatorBuilder(LobCreatorBuilderImpl.java:54)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl.<init>(JdbcEnvironmentImpl.java:271)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:114)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:88)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:259)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:233)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210)
    at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:51)
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:94)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:242)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.handleTypes(MetadataBuildingProcess.java:352)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:111)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:861)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:888)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.__createEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:40002)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.__createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:42002)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1758)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1695)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:573)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$109/1509220174.getObject(Unknown Source)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859)
    at org.springframework.context.support.AbstractApplicationContext.__refresh(AbstractApplicationContext.java:550)
    at org.springframework.context.support.AbstractApplicationContext.jrLockAndRefresh(AbstractApplicationContext.java:40002)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:41008)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:330)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1258)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
    at cn.mariojd.nearjob.NearjobApplication.main(NearjobApplication.java:19)
Caused by: java.sql.SQLFeatureNotSupportedException: 这个 org.postgresql.jdbc.PgConnection.createClob() 方法尚未被实作。
    at org.postgresql.Driver.notImplemented(Driver.java:688)
    at org.postgresql.jdbc.PgConnection.createClob(PgConnection.java:1269)
    ... 51 common frames omitted

  这个错误确实有点奇怪,不过好在Github上已经有相关Issue,有兴趣的可以去看看,该问题的解决方法是添加配置项:spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: true

JPA实体继承映射数据表

  当多个实体间有多个属性相同时,可以考虑抽取抽象实体类的方式复用属性定义,并在抽象父类上使用@MappedSuperclass注解(注意此父类不能再标注@Entity@Table注解):

  • BaseEntity
@Data
@MappedSuperclass
public abstract class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private Integer gender;

    @Column(name = "username")
    private String name;

}
  • Student
@Entity
@Table
@Data
public class Student extends BaseEntity {

    private Integer score;

}
  • Teacher
@Entity
@Table
@Data
public class Teacher extends BaseEntity {

    private String phone;

}

  此外,JPA中还有不同的遗传策略来解决多实体间的继承映射关系,同样可以实现上述一样的效果(@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)):

JPA InheritanceType
JPA InheritanceType

Spirng Data抽取抽象Repository

  这种情况跟上面那种情况有一定的关联,我们借助泛型来解决,首先建立一个BaseDao(需指定为@NoRepositoryBean):

@NoRepositoryBean
public interface BaseDao<T extends BaseEntity> extends JpaRepository<T, Integer> {

    // 部分字段查询需添加相对应构造器
    @Query(value = "SELECT new #{#entityName}(id,username) FROM #{#entityName} WHERE username=?1")
    T findByUsername(String username);
    
    T findByIdAndUsername(int id,String username);

}

  这样多个子Dao只需要继承这个BaseDao便可以拥有这些扩展方法。

Reactive Web集成ES启动冲突

  该问题出现在使用webflux集成elasticsearch启动项目的时候,异常信息打印如下:

2018-08-30 08:43:20.286  INFO 16636 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.transport.Netty4Plugin]
2018-08-30 08:43:22.999  WARN 16636 --- [           main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchClient' defined in class path resource [org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'elasticsearchClient' threw exception; nested exception is java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]

  具体解决方案可参考如下:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        // 添加该配置项即可
        System.setProperty("es.set.netty.runtime.available.processors", "false");
        SpringApplication.run(DemoApplication.class, args);
    }
    
}

Spring Data Elasticsearch与ES mapping字段不一致

  如果没有主动创建mapping,Spring Data ES默认会在第一次添加数据的时候创建,对应mapping的字段名跟实体属性保持一致。如果原本已经创建好mapping,或是想自定义mapping字段跟实体属性的对照关系,这里有两种解决方案:

  • 方案1

  借助@JsonProperty更改ES字段与实体属性的映射关系

@Data
@Document(indexName = "school", type = "primary_school")
public class Student {

    @Id
    private String id;

    private Integer gender;

    @JsonProperty("student_name")
    private String studentName;

}
Elasticsearch _mpping
Elasticsearch _mpping
  • 方案2

  使用@JsonNaming注解并指定相应的映射策略。如果当前实体需要使用多个@JsonProperty才能定义这种关系,可以考虑使用这种更快捷的方式

@Data
@Document(indexName = "school", type = "primary_school")
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Student {

    @Id
    private String id;

    private Integer gender;
    
    private String studentName;

}
PropertyNamingStrategy
PropertyNamingStrategy

  从上图可以看到jackson包中已经定义好有五种不同的映射策略,如果都不满足实际需求的话还可以自行扩展,只需要继承PropertyNamingStrategyBase这个抽象类并重写它的translate()方法即可。

ES一个Index对应多个type问题

  如果出现下面这个错误信息,说明定义了多个Type对应在一个Index。实际上在ES6.0之后,官方已经不推荐这种映射关系。按以前那种思路,Index对应Database,然后type对应table的关系(所以一个database中有多个table,那么一个index也就可以有多个type)是不严谨的,在官网上有这个Reference

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.elasticsearch.repository.support.SimpleElasticsearchRepository]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Rejecting mapping update to [school] as the final mapping would have more than 1 type: [teacher, student]

参考链接

Spring-data自定义Repository
elasticsearch常见的问题
JPA实体继承实体的映射策略
SpringData ES 关于字段名和索引中的列名字不一致导致的查询问题

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
2月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
20天前
|
XML Java API
Spring Boot集成MinIO
本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
|
22天前
|
消息中间件 Java Kafka
什么是Apache Kafka?如何将其与Spring Boot集成?
什么是Apache Kafka?如何将其与Spring Boot集成?
53 5
|
24天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
36 1
|
25天前
|
消息中间件 监控 Java
您是否已集成 Spring Boot 与 ActiveMQ?
您是否已集成 Spring Boot 与 ActiveMQ?
48 0
|
1月前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
40 1
|
2月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
194 1
|
2月前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
390 11
|
2月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
122 0
|
2月前
|
开发框架 监控 搜索推荐
GoFly快速开发框架集成ZincSearch全文搜索引擎 - Elasticsearch轻量级替代为ZincSearch全文搜索引擎
本文介绍了在项目开发中使用ZincSearch作为全文搜索引擎的优势,包括其轻量级、易于安装和使用、资源占用低等特点,以及如何在GoFly快速开发框架中集成和使用ZincSearch,提供了详细的开发文档和实例代码,帮助开发者高效地实现搜索功能。
193 0
下一篇
DataWorks