我正在迁移到 SpringBoot 3.0.1。更新并用 jakarta.persistence
替换所有 javax.persistence
后,所有基于 org.springframework.data.jpa.domain.Specification
的过滤器不起作用,没有错误,只是没有过滤。
示例实体:
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
@Entity
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "system_name")
private String systemName;
@Column(name = "title")
private String title;
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
@ToString.Exclude
private List<User> users;
// equals hashcode
}
存储库和规范实现:
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import my.package.model.Role;
import my.package.repository.filter.RoleFilter;
import my.package.util.SqlUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RoleRepository extends JpaRepository<Role, Long>, JpaSpecificationExecutor<Role> {
record RoleSpecification(@NotNull RoleFilter filter) implements Specification<Role> {
@NotNull
@Override
public Predicate toPredicate(@NotNull Root<Role> root,
@NotNull CriteriaQuery<?> query,
@NotNull CriteriaBuilder builder) {
Predicate predicate = builder.conjunction();
List<Expression<Boolean>> exps = predicate.getExpressions();
filter.getTitle().ifPresent(title ->
exps.add(builder.like(builder.lower(root.get("title")), SqlUtils.toLikeLower(title))));
return predicate;
}
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleFilter {
private String title;
public Optional<String> getTitle() {
return Objects.isNull(title) || title.isEmpty() ? Optional.empty() : Optional.of(title);
}
}
及用法:
import static org.springframework.data.jpa.domain.Specification.where;
@Service
@RequiredArgsConstructor
public class RoleServiceImpl implements RoleService {
private final RoleRepository roleRepository;
@Override
public List<Role> list() {
RoleFilter filter = new RoleFilter();
filter.setTitle("test");
return roleRepository.findAll(where(new RoleSpecification(filter)));
}
}
hibernate 生成这个奇怪的查询:
select
g1_0.id,
g1_0.created_at,
g1_0.is_hidden,
g1_0.title
from
groups g1_0
where
1=1
order by
g1_0.id desc offset ? rows fetch first ? rows only
WHERE
语句中没有关于 title
的内容。
构建.gradle
plugins {
id 'org.springframework.boot' version '3.0.1'
id 'io.spring.dependency-management' version '1.0.14.RELEASE'
id 'java'
}
ext {
jupiterVersion = '5.9.1'
lombokVersion = '1.18.24'
openApiVersion = '1.6.14'
mapstructVersion = '1.5.3.Final'
lombokMapstructBindingVersion = "0.2.0"
}
group = 'my.package'
version = '1.0'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "org.springdoc:springdoc-openapi-ui:${openApiVersion}"
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
implementation 'com.google.guava:guava:31.1-jre'
implementation 'net.minidev:json-smart:2.4.8'
implementation 'io.nats:jnats:2.16.5'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'org.flywaydb:flyway-core:9.10.1'
implementation 'org.jetbrains:annotations:23.1.0'
compileOnly "org.projectlombok:lombok:${lombokVersion}"
runtimeOnly 'com.h2database:h2:2.1.214'
runtimeOnly 'org.postgresql:postgresql:42.5.1'
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok-mapstruct-binding:${lombokMapstructBindingVersion}"
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.batch:spring-batch-test'
testImplementation "org.junit.jupiter:junit-jupiter-api:${jupiterVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-engine:${jupiterVersion}"
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:${jupiterVersion}'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.8.1'
testCompileOnly 'junit:junit:4.12'
}
tasks.named('test') {
useJUnitPlatform()
}
没有错误,没有异常,没有警告,只是忽略所有条件。 为什么会发生这种情况以及如何解决?
最佳答案
恐怕以前从来不应该工作 - 您正在修改构成谓词的 boolean 表达式列表,并根据 javadoc此类修改不会影响结果查询:
Return the top-level conjuncts or disjuncts of the predicate. Returns empty list if there are no top-level conjuncts or disjuncts of the predicate. Modifications to the list do not affect the query.
Previous implementation of CompoundPredicate没有遵循上面提到的 JPA 契约(Contract):
@Override
public List<Expression<Boolean>> getExpressions() {
return expressions;
}
现在it does :
@Override
public List<Expression<Boolean>> getExpressions() {
return new ArrayList<>( predicates );
}
UPD。正确的实现应该是这样的:
@NotNull
@Override
public Predicate toPredicate(@NotNull Root<Role> root,
@NotNull CriteriaQuery<?> query,
@NotNull CriteriaBuilder builder) {
List<Predicate> exps = new ArrayList<>;
filter.getTitle().ifPresent(title ->
exps.add(builder.like(builder.lower(root.get("title")), SqlUtils.toLikeLower(title))));
return builder.and(exps.toArray(new Predicate[0]));
}
关于java - Spring Boot 3 JPA 规范未使用 jakarta 进行过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74946324/