1
0

feat: 优化查询方法

This commit is contained in:
2025-08-15 13:04:09 +08:00
parent 50043157df
commit 42f1d896af
6 changed files with 246 additions and 22 deletions

52
pom.xml
View File

@@ -15,7 +15,7 @@
<spring-boot.version>3.4.3</spring-boot.version>
<spring-cloud.version>2024.0.1</spring-cloud.version>
<eclipse-collections.version>11.1.0</eclipse-collections.version>
<querydsl.version>5.1.0</querydsl.version>
</properties>
<dependencies>
@@ -30,7 +30,21 @@
<dependency>
<groupId>com.blinkfox</groupId>
<artifactId>fenix-spring-boot-starter</artifactId>
<version>3.0.0</version>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
@@ -60,6 +74,40 @@
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<id>compile-dsl</id>
<phase>compile</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
<outputDirectory>target/generated-sources/java</outputDirectory>
</configuration>
</execution>
<execution>
<id>test-compile-dsl</id>
<phase>test-compile</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
<outputDirectory>target/generated-test-sources/java</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>${releases.id}</id>

View File

@@ -210,22 +210,22 @@ public class Query {
}
public static class Between {
private String start;
private String end;
private Object start;
private Object end;
public String getStart() {
public Object getStart() {
return start;
}
public void setStart(String start) {
public void setStart(Object start) {
this.start = start;
}
public String getEnd() {
public Object getEnd() {
return end;
}
public void setEnd(String end) {
public void setEnd(Object end) {
this.end = end;
}

View File

@@ -5,19 +5,44 @@ import java.util.Map;
import java.util.Optional;
public class ObjectHelper {
/**
* 判断对象是否为null
*
* @param obj 待检查的对象
* @return 如果对象为null返回true否则返回false
*/
public static boolean isNull(Object obj) {
return obj == null;
}
/**
* 判断对象是否不为null
*
* @param obj 待判断的对象
* @return 如果对象不为null则返回true否则返回false
*/
public static boolean isNotNull(Object obj) {
return !isNull(obj);
}
/**
* 判断对象是否为空
*
* @param obj 待判断的对象
* @return 如果对象为null或为空则返回true否则返回false
*/
public static boolean isEmpty(Object obj) {
// 首先判断对象是否为null
if (isNull(obj)) return true;
// 判断是否为集合类型
else if (obj instanceof Collection<?> collection) return collection.isEmpty();
// 判断是否为Map类型
else if (obj instanceof Map<?, ?> map) return map.isEmpty();
// 判断是否为字符序列类型
else if (obj instanceof CharSequence sequence) return sequence.isEmpty();
// 判断是否为各种基本类型数组
else if (obj instanceof Object[] array) return array.length == 0;
else if (obj instanceof byte[] array) return array.length == 0;
else if (obj instanceof short[] array) return array.length == 0;
@@ -27,10 +52,13 @@ public class ObjectHelper {
else if (obj instanceof double[] array) return array.length == 0;
else if (obj instanceof char[] array) return array.length == 0;
else if (obj instanceof boolean[] array) return array.length == 0;
// 判断是否为Optional类型
else if (obj instanceof Optional<?> optional) return optional.isEmpty();
// 其他情况认为对象不为空
else return false;
}
public static boolean isNotEmpty(Object obj) {
return !isEmpty(obj);
}
@@ -38,4 +66,44 @@ public class ObjectHelper {
public static <T> T defaultIfNull(final T object, final T defaultValue) {
return isNull(object) ? defaultValue : object;
}
/**
* 判断给定的类是否可比较
*
* @param clazz 待判断的类对象
* @return 如果类是枚举、字符序列、可比较接口的实现类或基本数据类型则返回true否则返回false
*/
public static boolean isComparable(Class<?> clazz) {
if (isNull(clazz)) return false;
// 判断类是否为可比较类型:枚举、字符序列、可比较接口实现类或基本数据类型
return clazz.isEnum() ||
CharSequence.class.isAssignableFrom(clazz) ||
Comparable.class.isAssignableFrom(clazz) ||
clazz.isPrimitive();
}
public static boolean isComparable(Object obj) {
if (isNull(obj)) return false;
return isComparable(obj.getClass());
}
public static boolean isCollection(Class<?> clazz) {
if (isNull(clazz)) return false;
return Collection.class.isAssignableFrom(clazz);
}
public static boolean isCollection(Object obj) {
if (isNull(obj)) return false;
return isCollection(obj.getClass());
}
public static boolean isString(Class<?> clazz) {
if (isNull(clazz)) return false;
return String.class.isAssignableFrom(clazz);
}
public static boolean isString(Object obj) {
if (isNull(obj)) return false;
return isString(obj.getClass());
}
}

View File

@@ -2,9 +2,10 @@ package com.lanyuanxiaoyao.service.template.repository;
import com.blinkfox.fenix.jpa.FenixJpaRepository;
import com.blinkfox.fenix.specification.FenixJpaSpecificationExecutor;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface SimpleRepository<E, ID> extends FenixJpaRepository<E, ID>, FenixJpaSpecificationExecutor<E>, QueryByExampleExecutor<E> {
public interface SimpleRepository<E, ID> extends FenixJpaRepository<E, ID>, FenixJpaSpecificationExecutor<E>, QueryByExampleExecutor<E>, QuerydslPredicateExecutor<E> {
}

View File

@@ -11,6 +11,7 @@ import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -101,7 +102,7 @@ public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity> implemen
var enumType = (Class<Enum>) javaType;
return Enum.valueOf(enumType, enumName);
} else {
throw new IllegalArgumentException("枚举类型字段需要String类型的值");
throw new IllegalArgumentException("枚举类型字段需要 String 类型的值");
}
}
return value;
@@ -120,10 +121,18 @@ public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity> implemen
queryable.getNotNullEqual().forEach(column -> predicates.add(builder.isNotNull(column(root, column))));
}
if (ObjectHelper.isNotEmpty(queryable.getEmpty())) {
queryable.getEmpty().forEach(column -> predicates.add(builder.isEmpty(column(root, column))));
queryable.getEmpty().forEach(column -> {
var path = this.<Collection<Object>>column(root, column);
checkCollection(path, column);
predicates.add(builder.isEmpty(path));
});
}
if (ObjectHelper.isNotEmpty(queryable.getNotEmpty())) {
queryable.getNotEmpty().forEach(column -> predicates.add(builder.isNotEmpty(column(root, column))));
queryable.getNotEmpty().forEach(column -> {
var path = this.<Collection<Object>>column(root, column);
checkCollection(path, column);
predicates.add(builder.isNotEmpty(path));
});
}
if (ObjectHelper.isNotEmpty(queryable.getEqual())) {
queryable.getEqual().forEach((column, value) -> {
@@ -138,22 +147,52 @@ public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity> implemen
});
}
if (ObjectHelper.isNotEmpty(queryable.getLike())) {
queryable.getLike().forEach((column, value) -> predicates.add(builder.like(column(root, column), value)));
queryable.getLike().forEach((column, value) -> {
var path = column(root, column);
checkString(path, column);
checkString(value, column);
predicates.add(builder.like(column(root, column), value));
});
}
if (ObjectHelper.isNotEmpty(queryable.getNotLike())) {
queryable.getNotLike().forEach((column, value) -> predicates.add(builder.notLike(column(root, column), value)));
queryable.getNotLike().forEach((column, value) -> {
var path = column(root, column);
checkString(path, column);
checkString(value, column);
predicates.add(builder.notLike(column(root, column), value));
});
}
if (ObjectHelper.isNotEmpty(queryable.getGreat())) {
queryable.getGreat().forEach((column, value) -> predicates.add(builder.greaterThan(column(root, column), (Comparable<Object>) value)));
queryable.getGreat().forEach((column, value) -> {
var path = this.<Comparable<Object>>column(root, column);
checkComparable(path, column);
checkComparable(value, column);
predicates.add(builder.greaterThan(path, (Comparable<Object>) value));
});
}
if (ObjectHelper.isNotEmpty(queryable.getLess())) {
queryable.getLess().forEach((column, value) -> predicates.add(builder.lessThan(column(root, column), (Comparable<Object>) value)));
queryable.getLess().forEach((column, value) -> {
var path = this.<Comparable<Object>>column(root, column);
checkComparable(path, column);
checkComparable(value, column);
predicates.add(builder.lessThan(path, (Comparable<Object>) value));
});
}
if (ObjectHelper.isNotEmpty(queryable.getGreatEqual())) {
queryable.getGreatEqual().forEach((column, value) -> predicates.add(builder.greaterThanOrEqualTo(column(root, column), (Comparable<Object>) value)));
queryable.getGreatEqual().forEach((column, value) -> {
var path = this.<Comparable<Object>>column(root, column);
checkComparable(path, column);
checkComparable(value, column);
predicates.add(builder.greaterThanOrEqualTo(path, (Comparable<Object>) value));
});
}
if (ObjectHelper.isNotEmpty(queryable.getLessEqual())) {
queryable.getLessEqual().forEach((column, value) -> predicates.add(builder.lessThanOrEqualTo(column(root, column), (Comparable<Object>) value)));
queryable.getLessEqual().forEach((column, value) -> {
var path = this.<Comparable<Object>>column(root, column);
checkComparable(path, column);
checkComparable(value, column);
predicates.add(builder.lessThanOrEqualTo(path, (Comparable<Object>) value));
});
}
if (ObjectHelper.isNotEmpty(queryable.getIn())) {
queryable.getIn().forEach((column, value) -> predicates.add(builder.in(column(root, column)).value(value)));
@@ -162,14 +201,65 @@ public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity> implemen
queryable.getNotIn().forEach((column, value) -> predicates.add(builder.in(column(root, column)).value(value).not()));
}
if (ObjectHelper.isNotEmpty(queryable.getBetween())) {
queryable.getBetween().forEach((column, value) -> predicates.add(builder.between(column(root, column), value.getStart(), value.getEnd())));
queryable.getBetween().forEach((column, value) -> {
var path = this.<Comparable<Object>>column(root, column);
checkComparable(path, column);
checkComparable(value, column);
predicates.add(builder.between(column(root, column), (Comparable<Object>) value.getStart(), (Comparable<Object>) value.getEnd()));
});
}
if (ObjectHelper.isNotEmpty(queryable.getNotBetween())) {
queryable.getNotBetween().forEach((column, value) -> predicates.add(builder.between(column(root, column), value.getStart(), value.getEnd())));
queryable.getNotBetween().forEach((column, value) -> {
var path = this.<Comparable<Object>>column(root, column);
checkComparable(path, column);
checkComparable(value, column);
predicates.add(builder.between(column(root, column), (Comparable<Object>) value.getStart(), (Comparable<Object>) value.getEnd()).not());
});
}
return predicates;
}
private void checkComparable(Path<?> path, String column) {
if (!ObjectHelper.isComparable(path.getJavaType())) {
throw new NotComparableException(column);
}
}
private void checkComparable(Object value, String column) {
if (!ObjectHelper.isComparable(value)) {
throw new NotComparableException(column);
}
}
private void checkComparable(Query.Queryable.Between value, String column) {
checkComparable(value.getStart(), column);
checkComparable(value.getEnd(), column);
}
private void checkCollection(Path<?> path, String column) {
if (!ObjectHelper.isCollection(path.getJavaType())) {
throw new NotCollectionException(column);
}
}
private void checkCollection(Object value, String column) {
if (!ObjectHelper.isCollection(value)) {
throw new NotCollectionException(column);
}
}
private void checkString(Path<?> path, String column) {
if (!ObjectHelper.isString(path.getJavaType())) {
throw new NotStringException(column);
}
}
private void checkString(Object value, String column) {
if (!ObjectHelper.isString(value)) {
throw new NotStringException(column);
}
}
protected List<Predicate> listPredicate(Root<ENTITY> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return new ArrayList<>(0);
}
@@ -237,7 +327,25 @@ public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity> implemen
}
public IdNotFoundException(Long id) {
super("ID为%d的资源不存在".formatted(id));
super("ID为 %d 的资源不存在".formatted(id));
}
}
public static final class NotComparableException extends RuntimeException {
public NotComparableException(String variable) {
super("变量 %s 不能比较".formatted(variable));
}
}
public static final class NotCollectionException extends RuntimeException {
public NotCollectionException(String variable) {
super("变量 %s 不是集合".formatted(variable));
}
}
public static final class NotStringException extends RuntimeException {
public NotStringException(String variable) {
super("变量 %s 不是字符串".formatted(variable));
}
}
}

View File

@@ -6,7 +6,6 @@ import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Entity
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)