Spring Data JPA @query - Baeldung
Spring Data JPA @query - Baeldung
Spring Data JPA @Query We’ll also show how to build a dynamic query when the @Query
annotation is not enough.
Further reading:
Derived Query Methods in Spring Data JPA
Repositories (/?post_type=post&p=53886)
Explore the query derivation mechanism in Spring Data JPA.
Last updated: April 12, 2024 Spring Data JPA @Modifying Annotation (/?
post_type=post&p=50307)
Create DML and DDL queries in Spring Data JPA by combining the
Written by: baeldung (https://www.baeldung.com/author/baeldung) @Query and @Modifying annotations
1. Overview
We can pass an additional parameter of type Sort to a Spring Data
method declaration that has the @Query annotation. It’ll be translated into
the ORDER BY clause that gets passed to the database.
ADVERTISING
It’s a good approach to place a query definition just above the method
inside the repository rather than inside our domain model as named
queries. The repository is responsible for persistence, so it’s a better place
to store these definitions.
2.1. JPQL
2.2. Native
We can use also native SQL to define our query. All we have to do is set (https://ads.freestar.com/?
the value of the nativeQuery attribute to true and define the native SQL utm_campaign=branding&utm_medium=banner&utm_source=baeldung.com&utm_co
query in the value attribute of the annotation: 3.1. Sorting for JPA Provided and Derived Methods
@Query(
For the methods we get out of the box such as findAll(Sort) or the ones
value = "SELECT * FROM USERS u WHERE u.status = 1",
nativeQuery = true)
that are generated by parsing method signatures, we can only use object
Collection<User> findAllActiveUsersNative(); properties to define our sort:
userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));
3. Define Order in a Query Now imagine that we want to sort by the length of a name property:
It’s crucial that we use JpaSort.unsafe() to create a Sort object instance.
userRepository.findAll(Sort.by("LENGTH(name)"));
When we use:
When we execute the above code, we’ll receive an exception:
Sort.by("LENGTH(name)");
org.springframework.data.mapping.PropertyReferenceException: No
property LENGTH(name) found for type User! then we’ll receive the same exception as we saw above for the findAll()
method.
When Spring Data discovers the unsafe Sort order for a method that uses
3.2. JPQL the @Query annotation, then it just appends the sort clause to the query
— it skips checking whether the property to sort by belongs to the domain
model.
When we use JPQL for a query definition, then Spring Data can handle
sorting without any problem — all we have to do is add a method
parameter of type Sort:
3.3. Native
@Query(value = "SELECT u FROM User u")
List<User> findAllUsers(Sort sort); When the @Query annotation uses native SQL, then it’s not possible to
define a Sort.
We can call this method and pass a Sort parameter, which will order the If we do, we’ll receive an exception:
result by the name property of the User object:
org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodEx
ception: Cannot use native queries with dynamic sorting and/or
pagination
As the exception says, the sort isn’t supported for native queries. The error
message gives us a hint that pagination will cause an exception too.
(https://ads.freestar.com/?
n=branding&utm_medium=lazyLoad&utm_source=baeldung.com&utm_content=baeldung_leaderboard_mid_3
userRepository.findAllUsers(Sort.by("name"));
And because we used the @Query annotation, we can use the same
method to get the sorted list of Users by the length of their names:
(https://ads.freestar.com/?
userRepository.findAllUsers(JpaSort.unsafe("LENGTH(name)")); However, there is a workaround that enables pagination, and we’ll cover it
utm_campaign=branding&utm_medium=banner&utm_source=baeldung.com&utm_content=baeldung_inc
in the next section.
class DTO {
private Long customer_id;
private Long order_id;
private Long product_id;
4. Joining Tables in a Query
public DTO(Long customer_id, Long order_id, Long product_id) {
this.customer_id = customer_id;
When we use join clauses in queries in the @Query annotation, there are this.order_id = order_id;
this.product_id = product_id;
multiple ways to define the select clause when handling columns from
}
multiple tables. }
@Entity
As can be seen, the ResultDTO class has a composite primary key which
is defined in a separate class DTO and referenced via @IdClass annotation.
This is only required if the repository method returns a DTO with Id as
composite keys.
4.2. Native
In the case when we want to use native queries to join tables and fetch
results using Spring Data JPA API, we cannot specify a subset of columns
to be selected for the objects in the select clause, rather, we have to
create repository methods like below:
@Query(value = "SELECT c.*, o.*, p.* "
5.1. JPQL
+ " from Customer c, CustomerOrder o ,Product p "
+ " where c.id=o.customer_id " Using pagination in the JPQL query definition is straightforward:
+ " and o.id=p.customerOrder_id "
+ " and c.id=?1 "
, nativeQuery = true) @Query(value = "SELECT u FROM User u ORDER BY id")
List<Map<String, Object>> findByCustomer(Long id); Page<User> findAllUsersWithPagination(Pageable pageable);
We can see that the return type of the method findByCustomer is a list of We can pass a PageRequest parameter to get a page of data.
maps where the keys in the map correspond to the column names in the
Pagination is also supported for native queries but requires a little bit of
nativeQuery. The list itself corresponds to the list of objects returned by
additional work.
the method.
In the nativeQuery approach, we do not need to create any DTO classes,
but at the same time, nativeQuery fetches all columns from all the tables 5.2. Native
being joined in the @Query.
We can enable pagination for native queries by declaring an additional
attribute countQuery.
This defines the SQL to execute to count the number of rows in the whole
result:
@Query(
value = "SELECT * FROM Users ORDER BY id",
countQuery = "SELECT count(*) FROM Users",
nativeQuery = true)
Page<User> findAllUsersWithPagination(Pageable pageable);
(https://ads.freestar.com/?
paign=branding&utm_medium=lazyLoad&utm_source=baeldung.com&utm_content=baeldung_incontent_2 5.3. Spring Data JPA Versions Prior to 2.0.4
5. Pagination The above solution for native queries works fine for Spring Data JPA
versions 2.0.4 and later.
@Query(
value = "SELECT * FROM Users ORDER BY id \n-- #pageable\n",
countQuery = "SELECT count(*) FROM Users",
nativeQuery = true) (https://ads.freestar.com/? (https://ads.freestar.com/?
paign=branding&utm_medium=lazyLoad&utm_source=baeldung.com&utm_content=baeldung_incontent_3
Page<User> findAllUser sWithP agination (Pageable pageable); utm_campaign=branding&utm_medium=lazyLoad&utm_source=baeldung.com&utm_content=baeldung_in
@Query("SELECT u FROM User u WHERE u.status = ?1")
User findUserByStatus(Integer status);
In the above example, we add @Query("SELECT u FROM User u WHERE u.status = ?1 and u.name = ?2")
User findUserByStatusAndName(Integer status, String name);
\n-- #pageable\n
For the above queries, the status method parameter will be assigned to
as the placeholder for the pagination parameter. This tells Spring Data JPA the query parameter with index 1, and the name method parameter will
how to parse the query and inject the pageable parameter. This solution be assigned to the query parameter with index 2.
works for the H2 database.
We’ve covered how to create simple select queries via JPQL and native
SQL. Next, we’ll show how to define additional parameters. 6.2. Native
Indexed parameters for the native queries work exactly in the same way
as for JPQL:
6. Indexed Query Parameters
@Query(
value = "SELECT * FROM Users u WHERE u.status = ?1",
There are two possible ways that we can pass method parameters to our
nativeQuery = true)
query: indexed and named parameters. User findUserByStatusNative(Integer status);
In this section, we’ll cover indexed parameters.
In the next section, we’ll show a different approach: passing parameters
via name.
6.1. JPQL
7.1. JPQL @Query(value = "SELECT * FROM Users u WHERE u.status = :status and
u.name = :name",
nativeQuery = true)
As mentioned above, we use the @Param annotation in the method
User findUserByStatusA(https:/
ndNameN/ads.freestar.com/?
amedParamsNative(
declaration to match parameters defined by name in JPQL with @Param("status") Integer status, @Param("name") String name);
utm_campaign=branding&utm_medium=lazyLoad&utm_source=baeldung.com&utm_content=baeldung_in
parameters from the method declaration:
@Query("SELECT u FROM User u WHERE u.status = :status and u.name = In this case, we can define a query method that takes Collection as a
:name") parameter:
User findUserByUserStatusAndUserName(@Param("status") Integer
userStatus,
@Query(value = "SELECT u FROM User u WHERE u.name IN :names")
@Param("name") String userName);
List<User> findUserByNameList(@Param("names") Collection<String>
names);
7.2. Native As the parameter is a Collection, it can be used with List, HashSet, etc.
Next, we’ll show how to modify data with the @Modifying annotation.
For the native query definition, there is no difference in how we pass a
parameter via the name to the query in comparison to JPQL — we use the
@Param annotation:
9. Update Queries With @Modifying
We can use the @Query annotation to modify the state of the database
by also adding the @Modifying annotation to the repository method.
9.1. JPQL To perform an insert operation, we have to both apply @Modifying and
use a native query since INSERT is not a part of the JPA interface (/jpa-
insert):
The repository method that modifies the data has two differences in
comparison to the select query — it has the @Modifying annotation and,
of course, the JPQL query uses update instead of select: @Modifying
@Query(
value =
@Modifying "insert into Users (name, age, email, status) values (:name,
@Query("update User u set u.status = :status where u.name = :name") :age, :email, :status)",
int updateUserSetStatusForName(@Param("status") Integer status, nativeQuery = true)
@Param("name") String name); void insertUser(@Param("name") String name, @Param("age") Integer
age,
@Param("status") Integer status, @Param("email") String email);
The return value defines how many rows are updated by the execution of
the query. Both indexed and named parameters can be used inside
update queries.
For example, let’s imagine a situation where we need to select all the
users whose email is LIKE one from a set defined at runtime — email1,
email2, …, emailn:
return entityManager.createQuery(query)
.getResultList();
(https://ads.freestar.com/? }
paign=branding&utm_medium=lazyLoad&utm_source=baeldung.com&utm_content=baeldung_incontent_7 }
public interface UserRepositoryCustom {
List<User> findUserByEmails(Set<String> emails);
} As shown above, we leveraged the JPA Criteria API (/hibernate-
criteria-queries) to build our dynamic query.
And then we’ll implement it: Also, we need to make sure to include the Impl postfix in the class
name. Spring will search the UserRepositoryCustom implementation as
UserRepositoryCustomImpl. Since fragments are not repositories by
themselves, Spring relies on this mechanism to find the fragment
implementation.
Notice that all the query methods from section 2 through section 7 are in
the UserRepository.
So, now we’ll integrate our fragment by extending the new interface in the
UserRepository:
public interface UserRepository extends JpaRepository<User, Integer>,
UserRepositoryCustom {
// query methods from section 2 - section 7
}
userRepository.findUserByEmails(emails);
11. Conclusion