springboot+querydsl+jpa+pgsql自定义函数完成jsonb类型指定字段like(模糊匹配)

​ 记录在querydsl+jpa+pgsql的项目中、有事会用到pgsql的特有的数据类型比如jsonb类型、然有一些特殊的查询querydsl是不支持的、这就需要我们自定义函数来实现。

例如:在pgsql使用jsonb数据接口实现模糊查询其中的一个字段值。

表&实体信息如下:

sql 复制代码
create table if not exists rule_trigger_record
(
    id               bigint not null
        constraint rule_trigger_record_pk primary key,
    segment_field    varchar(32)[],
    involved_company jsonb,
    event_title      varchar(256)
);

comment on column rule_trigger_record.id is '记录表ID';

comment on column rule_trigger_record.segment_field is '细分领域';

comment on column rule_trigger_record.involved_company is '涉及企业信息';

comment on column rule_trigger_record.event_title is '标题';
java 复制代码
@TypeDefs({
        @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
        @TypeDef(name = "list-array", typeClass = ListArrayType.class)
})
@Data
@Entity
@Table(name = "rule_trigger_record")
public class RuleTriggerRecord {
    
    @Id
    @Column
    private Long id;
    
    /**
     * 事件标题 @Transient
     */
    private String eventTitle;

    /**
     * 涉及公司
     */
    @Type(type = "jsonb")
    @Column(name = "involved_company", columnDefinition = "jsonb")
    private List<InvolvedCompany> involvedCompany;
    
    /**
     * 细分领域
     */
    @Type(type = "list-array")
    @Column(name = "segment_field")
    private List<String> segmentField;


    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class InvolvedCompany {

        private Long companyId;

        private String companyName;

    }


}

可能需要这个依赖、来支持复杂类型:

xml 复制代码
<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.12.0</version>
</dependency>

就是说很快咱就能写出来、SQL语句的一个写法完成需求:

sql 复制代码
SELECT *
FROM rule_trigger_record
WHERE EXISTS(SELECT 1 FROM jsonb_array_elements(involved_company) AS ic WHERE ic ->> 'companyName' LIKE '爱奇艺%');

但是在querydsl+jpa的写法就要想麻烦一点了、因要尽可能的不出现自定义SQL,不便于维护。

所以使用jpa的自定义函数步骤如下:

  • 自定义函数

  • Custom PostgreSQL Dialect 自定义SQL方言

  • 自定义函数使用

一、自定义函数

新建类JsonbFieldLikeFunction继承SQLFunction

java 复制代码
/**
 * jsonb 指定字段 like 
 *
 * @author 傲寒
 * @date 2023/06/09
 */
public class JsonbFieldLikeFunction implements SQLFunction {

    @Override
    public boolean hasArguments() {
        return true;
    }

    @Override
    public boolean hasParenthesesIfNoArguments() {
        return true;
    }

    @Override
    public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
        return BooleanType.INSTANCE;
    }

    @Override
    public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) throws QueryException {
        if (arguments.size() != 3) {
            throw new IllegalArgumentException("jsonb_field_like requires 3 arguments!");
        }
        //最终语句 EXISTS(SELECT 1 FROM jsonb_array_elements({0}) AS ic WHERE ic ->> {1} LIKE '%{2}%')
        return "EXISTS(SELECT 1 FROM jsonb_array_elements(" + arguments.get(0).toString() + ") AS ic " +
                "WHERE ic ->> " + arguments.get(1).toString() + " LIKE concat('%'," + arguments.get(2).toString() + ",'%'))";
    }
}
二、自定义数据方言并配置

继承PostgreSQL95Dialect提供的方言配置

java 复制代码
public class CustomPostgreSQLDialect extends PostgreSQL95Dialect {

    public CustomPostgreSQLDialect() {
        super();
        this.registerFunction("jsonb_field_like", new JsonbFieldLikeFunction());
    }

}

然后再springboot配置文件中指定方言:

yaml 复制代码
spring:
  jpa:
    properties:
      hibernate:
      	# 方言配置全限定类名
        dialect: xxx.xxx.xxx.dialect.CustomPostgreSQLDialect
三、自定义函数使用

配置JPAQueryFactory的bean

java 复制代码
@Configuration
@Slf4j
public class JpaConfig  {

    @Bean
    public JPAQueryFactory qFactory(EntityManager entityManager) {
        return new JPAQueryFactory(entityManager);
    }

}

querydsl测试编写:

java 复制代码
 public void test() {
        //Q类
        QRuleTriggerRecord qRuleTriggerRecord = QRuleTriggerRecord.ruleTriggerRecord;
        String companyName = "爱奇艺";
        final BooleanTemplate template = Expressions.booleanTemplate(
                "jsonb_field_like({0},{1},{2})",
                qRuleTriggerRecord.involvedCompany,
                "companyName",
                companyName
        );
        BooleanBuilder whereEx = new BooleanBuilder();
        whereEx.and(template.isTrue());
        final List<RuleTriggerRecord> fetch = qFactory.selectFrom(qRuleTriggerRecord)
                .where(whereEx)
                .fetch();
        System.out.println(fetch);
    }

​ 这样就完成了自定义函数使用、反正pgsql提供很多特殊的数据类型(数组、坐标等)有时写法比较特殊、我们都可以采用这样的方式进行查询。

相关推荐
阿乾之铭21 小时前
通过Django 与 PostgreSQL 进行WEB开发详细流程
python·postgresql·django
TMDOG6661 天前
PostgreSQL 学习笔记:PostgreSQL 主从复制
笔记·学习·postgresql
大霸王龙1 天前
django+postgresql
数据库·后端·python·postgresql·django
喵手2 天前
PostgreSQL 增量备份:保护你的数据资产
数据库·postgresql
君败红颜3 天前
MySQL 和 PostgreSQL 的对比概述
数据库·mysql·postgresql
MMMMMMMMMMemory3 天前
服务器内存不够导致postgresql进程被kill的问题记录
运维·服务器·postgresql
PGCCC3 天前
【PGCCC】Postgresql 缓存池原理
spring·缓存·postgresql
DBA实战3 天前
PostgreSQL核心揭秘(二)-进程和内存架构
数据库·postgresql·架构
君败红颜3 天前
PostgreSQL 触发器的深入探讨
数据库·postgresql
高铭杰4 天前
Postgresql源码(137)执行器参数传递与使用
数据库·postgresql·参数·param·paramlistinfo