moh*_*1m2 39 spring hibernate jpql spring-data spring-data-jpa
我想在存储库层中编写一些查询方法.此方法必须忽略null参数.例如:
List<Foo> findByBarAndGoo(Bar barParam, @optional Goo gooParam);
Run Code Online (Sandbox Code Playgroud)
此方法必须通过以下条件返回Foo:
bar == barParam && goo == gooParam;
Run Code Online (Sandbox Code Playgroud)
如果gooParam不为null.如果gooParam为null,则条件更改为:
bar == barParam;
Run Code Online (Sandbox Code Playgroud)
有什么解决方案吗?有人能帮我吗?
cha*_*erb 26
我不相信你能用查询定义的方法名称方法做到这一点.从文档(参考):
尽管获取从方法名称派生的查询非常方便,但是可能面临这样的情况:方法名称解析器不支持要使用的关键字,或者方法名称会不必要地变得难看.因此,您可以通过命名约定使用JPA命名查询(请参阅使用JPA NamedQueries获取更多信息)或者使用@Query注释查询方法
我认为你有这种情况,所以下面的答案使用@Query注释方法,这几乎和方法名称方法(参考)一样方便.
@Query("select foo from Foo foo where foo.bar = :bar and "
+ "(:goo is null or foo.goo = :goo)")
public List<Foo> findByBarAndOptionalGoo(
@Param("bar") Bar bar,
@Param("goo") Goo goo);
Run Code Online (Sandbox Code Playgroud)
Vit*_*eis 14
作为@chaserb的答案的补充,我个人会将参数添加为Java8可选类型,以使其在方法的签名中显式显示作为可选过滤器的语义.
@Query("select foo from Foo foo where foo.bar = :bar and "
+ "(:goo is null or foo.goo = :goo)")
public List<Foo> findByBarAndOptionalGoo(
@Param("bar") Bar bar,
@Param("goo") Optional<Goo> goo);
Run Code Online (Sandbox Code Playgroud)
Sha*_*tel 12
回答太晚了.不确定Bar和Goo之间的关系.检查示例是否可以帮助您.
它对我有用.我有类似的情况,实体用户有一组属性,并且有findAll方法根据属性搜索用户(这是可选的).
例,
Class User{
String firstName;
String lastName;
String id;
}
Class UserService{
// All are optional
List<User> findBy(String firstName, String lastName, String id){
User u = new User();
u.setFirstName(firstName);
u.setLastName(lastName);
u.setId(id);
userRepository.findAll(Example.of(user));
// userRepository is a JpaRepository class
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用JpaSpecificationExecutor//import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
第 1 步JpaSpecificationExecutor:在您的 JPA 存储库中实施
前任:
public interface TicketRepo extends JpaRepository<Ticket, Long>, JpaSpecificationExecutor<Ticket> {
Run Code Online (Sandbox Code Playgroud)
第 2 步现在,要根据可选参数获取票证,您可以使用 CriteriaBuilder 构建规范查询
前任:
public Specification<Ticket> getTicketQuery(Integer domainId, Calendar startDate, Calendar endDate, Integer gameId, Integer drawId) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("domainId"), domainId));
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));
if (gameId != null) {
predicates.add(criteriaBuilder.equal(root.get("gameId"), gameId));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
Run Code Online (Sandbox Code Playgroud)
步骤3:将Specification实例传递给jpaRepo.findAll(specification),它将返回实体对象的列表(运行示例中的Tickets)
ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll
Run Code Online (Sandbox Code Playgroud)
已经有很多很棒的答案了,但我专门使用 @Pankaj Garg 的答案(使用 Spring 规范 API)实现了这一点。我在我的答案中添加了一些用例
首先,我创建了几个实体,特别是Ticket、Movie和Customer。这里没什么特别的:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
@Entity
@Table(name = "ticket", schema = "public")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Ticket implements Serializable {
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id", nullable = false)
private UUID id;
@JoinColumn(name = "movie_id", referencedColumnName = "id", nullable = false)
@ManyToOne(fetch = FetchType.EAGER)
private Movie movie;
@JoinColumn(name = "customer_id", referencedColumnName = "id", nullable = false)
@ManyToOne(fetch = FetchType.EAGER)
private Customer customer;
@Column(name = "booking_date")
@Temporal(TemporalType.TIMESTAMP)
private Date bookingDate;
}
Run Code Online (Sandbox Code Playgroud)
电影:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Entity
@Table(name = "movie", schema = "public")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Movie implements Serializable {
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id", nullable = false)
private UUID id;
@Basic(optional = false)
@NotNull
@Size(max = 100)
@Column(name = "movie_name", nullable = false, length = 100)
private String movieName;
}
Run Code Online (Sandbox Code Playgroud)
顾客:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Entity
@Table(name = "customer", schema = "public")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Customer implements Serializable {
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id", nullable = false)
private UUID id;
@Basic(optional = false)
@NotNull
@Size(max = 100)
@Column(name = "full_name", nullable = false, length = 100)
private String fullName;
}
Run Code Online (Sandbox Code Playgroud)
然后我创建一个类,其中包含我希望过滤的参数的字段:
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
import java.util.UUID;
@Data
@AllArgsConstructor
public class TicketFilterParam {
private UUID movieId;
private UUID customerId;
private Date start;
private Date end;
}
Run Code Online (Sandbox Code Playgroud)
接下来我创建一个类来Specification根据过滤器参数生成一个。请注意访问嵌套对象的方式以及将排序添加到查询的方式。
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TicketSpecifications {
public static Specification<Ticket> getFilteredTickets(TicketFilterParam params) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getMovieId() != null) {
predicates.add(criteriaBuilder.equal(root.get("movie").<UUID> get("id"), params.getMarketerId()));
}
if (params.getCustomerId() != null) {
predicates.add(criteriaBuilder.equal(root.get("customer").<UUID> get("id"), params.getDepotId()));
}
if (params.getStart() != null && params.getEnd() != null) {
predicates.add(criteriaBuilder.between(root.get("bookingDate"), params.getStart(), params.getEnd()));
}
criteriaQuery.orderBy(criteriaBuilder.desc(root.get("bookingDate")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
Run Code Online (Sandbox Code Playgroud)
接下来我定义存储库接口。JpaRepository这不仅会JpaSpecificationExecutor:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface TicketRepository extends JpaRepository<Ticket, UUID>, JpaSpecificationExecutor<Ticket> {
}
Run Code Online (Sandbox Code Playgroud)
最后,在某些服务类中,我得到如下结果:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
@Service
public class TicketService {
@Autowired
private TicketRepository ticketRepository;
public Page<Ticket> getTickets(TicketFilterParam params, PageRequest pageRequest) {
Specification<Ticket> specification = TicketSpecifications.getFilteredTickets(params);
return ticketRepository.findAll(specification, pageRequest);
}
}
Run Code Online (Sandbox Code Playgroud)
PageRequest并且TicketFilterParam可能从休息端点上的一些参数和值获得。
您可以自己编写代码,只需几行:
List<Foo> findByBarAndOptionalGoo(Bar bar, Goo goo) {
return (goo == null) ? this.findByBar(bar) : this.findByBarAndGoo(bar, goo);
}
Run Code Online (Sandbox Code Playgroud)
否则,我不知道 Spring-Data 是否支持开箱即用。