This is the post #3 of the series "Querying your Spring Data JPA Repository".
If you're following the series, by now you have an app with a list of Restaurants. The list is fetched using the method findAll()
which you did not implement, because it was provided by Spring Data JPA when you created the RestaurantRepository
class extending Spring's JpaRepository
class.
Take a look at the previous post if you haven't already:
Let's say that now you have to add a search bar on top of the list in order to filter it. The search must match any Restaurant name that contains the text the user types.
As you can see, if the user types Ma
three Restaurants are shown: Mamma Mia
; La Maison du Croissant
; and Marcante Pizzaria
. They all contain Ma
in their names.
Well, let's prepare our app to work with this new requirement.
Let's add a search bar with the following Thymeleaf code on the index.html
file:
<form th:action="@{/search}">
Search by:
<select name="field">
<option value="name">Name</option>
</select>
<input placeholder="Your search query" th:name="query" th:value="${query}" type="text"/>
<input type="submit" value="Search"/>
</form>
That's a simple form that hits on /search
address with two request parameters:
field
which contains the name of the field the user wants to search on (supporting only name
for now).query
containing whatever the user types into the search field.The URL after clicking Search with Ma
on the search field would be https://whatever-your-server-is/search?field=name&query=Ma
.
We need a method on the IndexPage.java
controller to handle the search for the user. This is the method that does the trick:
@RequestMapping("/search")
public String indexWithQuery(@RequestParam("query") String query,
@RequestParam("field") String field,
Model model) {
if (field.equals("name")) {
// TODO add search by name here.
}
model.addAttribute("field", field);
model.addAttribute("query", query);
return "index";
}
Let's understand what's important here:
@RequestMapping("/search")
ties this method to the request that the form we added earlier will perform.@RequestParam("query") String query
and @RequestParam("field") String field
bind the request params (the variables after ?
on the URL) to Java variables we can use.The rest of the code:
Well, looks like we have to do the heavy work now, which is to find all the Restaurants containing a certain string on their names.
We do that with this huge and complicated code on the RestaurantRepository.java
interface:
List<Restaurant> findAllByNameContaining(String query);
And now, by replacing that TODO
we added earlier on the IndexPage.java
controller with the following:
model.addAttribute("restaurants", restaurantRepository.findAllByNameContaining(query));
... your new search is working! Want to try?
It's quite simple, actually: Spring Data parses the method name based on certain criteria and creates a query for you. Here's how it goes for this example (findAllByNameContaining(String name)
):
find
determines that entities should be returned. Could also be read
, query
and get
to the same effect. If it was count
would return the number of entities.All
is ignored. Note that some keywords may be used here.By
indicates to the parser the start of the actual criteria.Name
tells Spring Data that there's a property called name
on the underlying entity of this repository (Restaurant
) that will be used as search criteria. In other words, there'll be a where
clause in that property.Containing
specifies the operator for the where
clause. If not provided, =
is implied.(String name)
indicates the parameter that will be received in runtime to use as value on the where
clause.I explained, but it's pretty self explanatory, don't you think? Well, with that in mind, let's think of some other valid methods following the same structure:
findAllByCuisineNameContaining(String cuisine)
returns Restaurants which cuisine contains the provided value/string. Notice we are querying Restaurants but using fields from the Cuisine entity because Spring Data traverses nested properties!findAllByDeliveryFeeIsLessThanEqual(BigDecimal deliveryFee)
returns Restaurants with delivery fee less than or equal the provided deliveryFee
value. Notice the parameter type matches the entity's property type.Now you have more options on your search:
Other valid methods:
countByCuisineName(String cuisine)
to return the number of Restaurants with a given cuisine type.findTopByCuisineNameOrderByDeliveryFeeAsc(String cuisine)
to return the restaurant with the cheapest delivery fee of a given cuisine.And their usage:
The repository with all the queries is not even a concrete class, there's no implementation on your code:
public interface RestaurantRepository extends JpaRepository<Restaurant, Long> {
List<Restaurant> findAllByNameContaining(String query);
List<Restaurant> findAllByCuisineNameContaining(String cuisine);
List<Restaurant> findAllByDeliveryFeeIsLessThanEqual(BigDecimal deliveryFee);
long countByCuisineName(String cuisine);
Restaurant findTopByCuisineNameOrderByDeliveryFeeAsc(String cuisine);
}
You can find more information, including a list of supported keywords on the official documentation.
The working app is here (wait for Heroku to load the app, it takes a few seconds on the free tier).
Special thanks to our guest blogger Bruno Drugowick a Computer Scientist for his contribution to the Ronald James Blog this week.
Visit the original link for this blog here.
We are a leading niche digital & tech recruitment specialist for the North East of England. We Specialise in the acquisition of high-performing technology talent across a variety of IT sectors including Digital & Technology Software Development.
Our ultimate goal is to make a positive impact on every client and candidate we serve - from the initial call and introduction, right up to the final delivery, we want our clients and candidates to feel they have had a beneficial and productive experience.
If you’re looking to start your journey in sourcing talent or find your dream job, you’ll need a passionate, motivated team of experts to guide you. Check out our Jobs page for open vacancies. If interested, contact us or call 0191 620 0123 for a quick chat with our team.
Follow us on our blog, Facebook, LinkedIn, Twitter or Instagram to follow industry news, events, success stories and new blogs releases.
Back to Blog