如何将子查询添加到 Java 中的规范中

how to add subquery into specification in Java

我希望有子查询,它可以按名称过滤角色。

我有一个休息控制器的方法,returns 演员列表作为 JSON 来自基于 movieId 的电影。我尝试添加过滤器作为规范,但我不知道如何编写正确的查询。基于“”,我找到了子查询的解决方案,return 使我所有的演员都可以根据 movieId 找到合适的电影。现在我试着写这个查询。

演员实体

@Data
@NoArgsConstructor
@Entity
@Table(name = "actors")
public class Actor implements Serializable {

private static final long serialVersionUID = 6460140826650392604L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "actor_id")
private Long actorId;

@Column(name = "first_name")
private String firstName;

@ManyToMany(mappedBy = "actors")
@ToString.Exclude
private List<Movie> movie = new ArrayList<>();

@JsonIgnore
public List<Movie> getMovie() {
    return this.movie;
}
}

电影实体

@Data
@Entity
@NoArgsConstructor
@Table(name = "movies")

public class Movie implements Serializable {

private static final long serialVersionUID = 3683778473783051508L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "movie_id")
private Long movieId;

private String title;

@ManyToMany(cascade = { CascadeType.ALL })
@JoinTable(name = "movies_actors"
        , joinColumns = { @JoinColumn(name = "movie_id") }
        , inverseJoinColumns = { @JoinColumn(name = "actor_id") })

private List<Actor> actors = new ArrayList<>();

@JsonIgnore
public List<Actor> getActors() {
    return this.actors;
}
}

//休息控制器

@CrossOrigin(origins = "http://localhost:3000")
@RestController
@RequestScope
@RequestMapping("/rest")
public class ActorRestController {

private ActorService actorService;
private MovieService movieService;

@Autowired
public ActorRestController(ActorService actorService, MovieService movieService) {
    this.actorService = actorService;
    this.movieService = movieService;
}
.
.
.

@GetMapping("movies/{movieId}/actors")
public ResponseEntity<Page<Actor>> getAllActorsFromMovieByIdMovie(@PathVariable(name = "movieId") Long movieId, Pageable pageable) {
    Optional<Movie> movieFromDataBase = movieService.findMovieById(movieId);
    if (movieFromDataBase.isPresent()) {
        return new ResponseEntity<>(actorService.findAllActors(ActorSpec.query(movieId), pageable), HttpStatus.OK);
    } else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}
.
.
}

// 演员规范

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ActorSpec {

public static Specification<Actor> query(final Long movieId) {
    return (root, query, cb) -> {
        query.distinct(true);
        Subquery<Movie> movieSubQuery = query.subquery(Movie.class);
        Root<Movie> movie = movieSubQuery.from(Movie.class);
        Expression<List<Actor>> actors = movie.get("actors");
        movieSubQuery.select(movie);
        movieSubQuery.where(cb.equal(movie.get("movieId"), movieId), cb.isMember(root, actors));

        return cb.exists(movieSubQuery);
    };
 }


}

我想,我的代码将 return 按名称过滤演员,例如:

http://localhost:8080/rest/movies/48/actors?name=Collin

会 return 我

 { "actorId": 159,
"firstName": "Collin",
"lastName": "Konopelski",
"age": 21
 },

但如果我没有发送任何请求参数 (http://localhost:8080/rest/movies/48/actors),让所有演员 return 我编程。我不想只为 @Requestparam 创建新端点,因为这个端点由在 React 中创建的 UI 使用。

谢谢!

好的,我找到了,

我的解决方案:

休息控制器

@GetMapping("movies/{movieId}/actors")
public ResponseEntity<Page<Actor>> getAllActorsFromMovieByIdMovie(@PathVariable(name = "movieId") Long movieId,
                                                                  @RequestParam(name = "name", required = false) String name,
                                                                  Pageable pageable) {
    Optional<Movie> movieFromDataBase = movieService.findMovieById(movieId);
    if (movieFromDataBase.isPresent()) {
        return new ResponseEntity<>(actorService.findAllActors(ActorSpec.query(movieId ,name), pageable), HttpStatus.OK);
    } else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

规格

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ActorSpec {

public static Specification<Actor> query(final Long movieId, String name) {
    return (root, query, cb) -> {
        Predicate predicateMovieID = getPredicateByMovieId(movieId, root, query, cb);

        if (Strings.isNotBlank(name)) {
            Predicate a = cb.and(predicateMovieID, cb.equal(root.get("firstName"), name));
            Predicate b = cb.and(predicateMovieID, cb.equal(root.get("lastName"), name));
            return cb.or(a,b);
        }
        return cb.and(predicateMovieID);
    };
}

private static Predicate getPredicateByMovieId(Long movieId, Root<Actor> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    query.distinct(true);
    Subquery<Movie> movieSubQuery = query.subquery(Movie.class);
    Root<Movie> movie = movieSubQuery.from(Movie.class);
    Expression<List<Actor>> actors = movie.get("actors");
    movieSubQuery.select(movie);
    movieSubQuery.where(cb.equal(movie.get("movieId"), movieId), cb.isMember(root, actors));
    return cb.exists(movieSubQuery);
    }
}