如何将子查询添加到 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);
}
}
我希望有子查询,它可以按名称过滤角色。
我有一个休息控制器的方法,returns 演员列表作为 JSON 来自基于 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);
}
}