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

相关推荐
喝醉酒的小白35 分钟前
PostgreSQL: 事务年龄
数据库·postgresql
Amd7944 小时前
PostgreSQL 的特点
postgresql·数据类型·并发控制·关系型数据库·安全性·可扩展性·数据库特性
程序员学习随笔20 小时前
PostgreSQL技术内幕21:SysLogger日志收集器的工作原理
数据库·postgresql
秦时明月之君临天下21 小时前
PostgreSQL标识符长度限制不能超过63字节
数据库·postgresql
Amd7941 天前
PostgreSQL 的历史
postgresql·开源软件·计算机科学·软件开发·关系型数据库·数据库技术·数据库历史
gis分享者1 天前
麒麟V10系统,postgres+postgis安装,保姆级教程,包含所有安装包
postgresql·安装·postgis·麒麟系统
赵渝强老师2 天前
【赵渝强老师】PostgreSQL的参数文件
数据库·postgresql
YRr YRr6 天前
解决 Ubuntu 20.04 上因 postmaster.pid 文件残留导致的 PostgreSQL 启动失败问题
linux·ubuntu·postgresql
vvw&6 天前
如何在 Ubuntu 22.04 上安装 pgAdmin:一个 PostgreSQL 管理工具
linux·运维·服务器·ubuntu·postgresql·开源软件·pgadmin
小袁搬码7 天前
PostgreSQL17.x数据库备份命令及语法说明
数据库·postgresql·pg数据库备份