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