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

时间:2019-07-18 10:56:18

标签: java json spring-data-jpa specifications

我希望有子查询,该子查询可以按名称过滤演员。

我有一个rest控制器的方法,该方法根据基于movieId的电影将演员列表作为JSON返回。我尝试添加过滤器作为规范,但是我不知道如何编写适当的查询。基于“ Spring Data JPA Specification for a ManyToMany Unidirectional Relationship”,我找到了子查询的解决方案,该查询将所有演员返回到基于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);
    };
 }


}

我想,我的代码将返回名称为ex的过滤演员:

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

将返回我

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

但如果我未发送任何请求参数(http://localhost:8080/rest/movies/48/actors),请让程序将所有参与者返回给我。我不想只为@Requestparam原因创建新的端点,这是由React中创建的UI使用的。

谢谢!

1 个答案:

答案 0 :(得分:0)

我发现了,

我的解决方案:

RestController

@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);
    }
}