我希望有子查询,该子查询可以按名称过滤演员。
我有一个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使用的。
谢谢!
答案 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);
}
}