1. Overview
In this tutorial, we’ll continue with the second part of Spring Data Querydsl Web Support. Here, we’ll focus on associated entities and how to create queries over HTTP.
Following the same configuration used in part one, we’ll create a Maven-based project. Please refer to the original article to check how to set up the basics.
2. Entities
First, let’s add a new entity (Address) creating a relationship between the user and her address. We’ve used the OneToOne relationship to keep it simple.
Consequently, we’ll have the following classes:
@Entity public class User { @Id @GeneratedValue private Long id; private String name; @OneToOne(fetch = FetchType.LAZY, mappedBy = "user") private Address addresses; // getters & setters }
@Entity public class Address { @Id @GeneratedValue private Long id; private String address; private String country; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; // getters & setters }
3. Spring Data Repositories
At this point, we have to create the Spring Data repositories, as usual, one for each entity. Note that these repositories will have the Querydsl configuration.
Let’s see the AddressRepository repository and explain how the framework configuration works:
public interface AddressRepository extends JpaRepository<Address, Long>, QueryDslPredicateExecutor<Address>, QuerydslBinderCustomizer<QAddress> { @Override default void customize(QuerydslBindings bindings, QAddress root) { bindings.bind(String.class) .first((SingleValueBinding<StringPath, String>) StringExpression::eq); } }
We’re overriding the customize() method to configure the default binding. In this case, we’ll customize the default method binding to be equals, for all String properties.
Once the repository is all set, we just have to add a @RestController to manage the HTTP queries.
4. Query Rest Controller
In part one, we explained the Query@RestController over user repository, here, we’ll just reuse it.
Also, we may want to query the address table; so for this, we’ll just add a similar method:
@GetMapping(value = "/addresses", produces = MediaType.APPLICATION_JSON_VALUE) public Iterable<Address> queryOverAddress( @QuerydslPredicate(root = Address.class) Predicate predicate) { BooleanBuilder builder = new BooleanBuilder(); return addressRepository.findAll(builder.and(predicate)); }
Let’s create some tests to see how this works.
5. Integration Testing
We’ve included a test to prove how Querydsl works. For this, we are using the MockMvc framework to simulate HTTP querying over user joining this entity with the new one: address. Therefore, we are now able to make queries filtering address attributes.
Let’s retrieve all users living in Spain:
/users?addresses.country=Spain
@Test public void givenRequest_whenQueryUserFilteringByCountrySpain_thenGetJohn() throws Exception { mockMvc.perform(get("/users?address.country=Spain")).andExpect(status().isOk()).andExpect(content() .contentType(contentType)) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].name", is("John"))) .andExpect(jsonPath("$[0].address.address", is("Fake Street 1"))) .andExpect(jsonPath("$[0].address.country", is("Spain"))); }
As a result, Querydsl will map the predicate sent over HTTP and generates the following SQL script:
select user0_.id as id1_1_, user0_.name as name2_1_ from user user0_ cross join address address1_ where user0_.id=address1_.user_id and address1_.country='Spain'
6. Conclusion
To sum up, we have seen that Querydsl offers to the web clients a very simple alternative to create dynamic queries; another powerful use of this framework.
In part I, we saw how to retrieve data from one table; consequently, now, we can add queries joining several tables, offering web-clients a better experience filtering directly over HTTP requests they make.
The implementation of this example can be checked in the GitHub project – this is a Maven-based project, so it should be easy to import and run as it is.