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提供很多特殊的数据类型(数组、坐标等)有时写法比较特殊、我们都可以采用这样的方式进行查询。

相关推荐
777VG16 小时前
PostgreSQL +martin将多张表输出成一个 MVT
前端·数据库·postgresql
IvorySQL17 小时前
PG 日报|优化缓冲区批量扫描,降低多套接字并发竞争
数据库·人工智能·postgresql·开源·区块链
muddjsv1 天前
SQLite与MySQL/PostgreSQL本质区别:运行模型、并发、部署、权限全方位对比
mysql·postgresql·sqlite
pxzsky2 天前
PG17数据库安装中分分词插件:pg_jieba
数据库·postgresql·pg_jieba
jun_bai2 天前
postgresql数据库免安装版安装到windows系统
数据库·postgresql
BullSmall2 天前
CentOS7 阿里云 PG 镜像 PG10.9 → PG17 完整升级指南
阿里云·postgresql·centos
瀚高PG实验室2 天前
DELETE 条件字段缺少索引导致 SuperSync 同步严重延迟
数据库·postgresql·瀚高数据库
IvorySQL2 天前
PG 日报|修复高 IO 并发场景,解决预读机制耗尽本地缓冲区缺陷
数据库·人工智能·sql·postgresql·区块链
想你依然心痛2 天前
电力能耗系统怎么搭,Django 加 PostgreSQL 入门指南
postgresql·django·电力能耗系统,后端开发
凌虚4 天前
基于 PostgreSQL WAL 构建 CDC 系统:原理与工程实现
数据库·后端·postgresql