xxxxxxxxxx
public interface RestaurantRepository extends JpaRepository<Restaurant, Long>,
JpaSpecificationExecutor<Restaurant> {
// old query methods omitted for brevity
}
xxxxxxxxxx
class RestaurantTopRatedSpecification implements Specification<Restaurant> {
public static final double MINIMAL_AVERAGE_RATING = 8.0;
@Override
public Predicate toPredicate(Root<Restaurant> restaurant,
CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
final var averageRating = criteriaBuilder.avg(restaurant.join(Restaurant_.RATINGS).get(Rating_.SCORE));
final var topRatedPredicate = criteriaBuilder.greaterThanOrEqualTo(averageRating, MINIMAL_AVERAGE_RATING);
query.groupBy(restaurant.get(Restaurant_.ID)).having(topRatedPredicate);
return null;
}
}
xxxxxxxxxx
class RestaurantNameSpecification implements Specification<Restaurant> {
private final String name;
public RestaurantNameSpecification(String name) {
this.name = name;
}
@Override
public Predicate toPredicate(Root<Restaurant> restaurant, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
final var nameExpression = criteriaBuilder.lower(restaurant.get(Restaurant_.name));
return criteriaBuilder.like(nameExpression, "%" + name.toLowerCase() + "%");
}
}
xxxxxxxxxx
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>
xxxxxxxxxx
public final class RestaurantSpecifications {
private RestaurantSpecifications() {
}
public static Specification<Restaurant> hasTopRating() {
return new RestaurantTopRatedSpecification();
}
}
xxxxxxxxxx
@Service
public class RestaurantService {
private final RestaurantRepository restaurantRepository;
// constructor ommited for brevity
public List<Restaurant> findTopRated() {
return restaurantRepository.findAll(where(hasTopRating()));
}
}
xxxxxxxxxx
@DataJpaTest
class RestaurantNameSpecificationTest {
@Autowired
private RestaurantRepository restaurantRepository;
@BeforeEach
void setup() {
restaurantRepository.save(new Restaurant("Café Java"));
restaurantRepository.save(new Restaurant("Spring Restaurant"));
restaurantRepository.save(new Restaurant("Jakarta Restaurant"));
}
@Test
void matchesName() {
final var topRestaurants = restaurantRepository.findAll(
where(nameIsLike("Restaurant"))
);
assertThat(topRestaurants, hasSize(2));
assertThat(topRestaurants, contains(
hasProperty("name", is("Spring Restaurant")),
hasProperty("name", is("Jakarta Restaurant"))
));
}
}