The previous part of this tutorial described how we can create database queries from the method names of our query methods.
Although that strategy has its advantages, it has its weaknesses as well. This blog post describes how we can avoid those weaknesses by using the @Query annotation.
We will also implement a simple search function that has two requirements:
- It must return todo entries whose title or description contains the given search term.
- The search must be case insensitive.
Let’s get started.
If you are not familiar with Spring Data JPA, you should read the following blog posts before you continue reading this blog post:
- Spring Data JPA Tutorial: Introduction provides a quick introduction to Spring Data JPA and gives an overview of the Spring Data repository interfaces.
- Spring Data JPA Tutorial: Getting the Required Dependencies describes how you can get the required dependencies.
- Spring Data JPA Tutorial: Configuration describes how you can configure the persistence layer of a Spring application that uses Spring Data JPA.
- Spring Data JPA Tutorial: Introduction to Query Methods describes how you can pass method parameters to your query methods and identifies the "legal" return values of Spring Data JPA query methods.
Creating Query Methods
We can configure the invoked database query by annotating the query method with the @Query annotation. It supports both JPQL and SQL queries, and the query that is specified by using the @Query annotation precedes all other query generation strategies.
In other words, if we create a query method called findbyId() and annotate it with the @Query annotation, Spring Data JPA won’t (necessarily) find the entity whose id property is equal than the given method parameter. It invokes the query that is configured by using the @Query annotation.
Example:
Let’s assume that our repository interface looks as follows:
import org.springframework.data.repository.Repository; import java.util.Optional; interface TodoRepository extends Repository<Todo, Long> { @Query("SELECT t FROM Todo t WHERE t.title = 'title'") public List<Todo> findById(); }
Even though the findById() method follows the naming convention that is used to create database queries from the method name of the query method, the findById() method returns todo entries whose title is 'title', because that is the query which is specified by using the @Query annotation.
Let’s find out how we can create both JPQL and SQL queries with the @Query annotation.
Creating JPQL Queries
We can create a JPQL query with the @Query annotation by following these steps:
- Add a query method to our repository interface.
- Annotate the query method with the @Query annotation, and specify the invoked query by setting it as the value of the @Query annotation.
The source code of our repository interface looks as follows:
import org.springframework.data.repository.Repository; import java.util.Optional; interface TodoRepository extends Repository<Todo, Long> { @Query("SELECT t FROM Todo t WHERE t.title = 'title'") public List<Todo> findByTitle(); }
Creating SQL Queries
We can create a SQL query with the @Query annotation by following these steps:
- Add a query method to our repository interface.
- Annotate the query method with the @Query annotation, and specify the invoked query by setting it as the value of the @Query annotation's value attribute.
- Set the value of the @Query annotation’s nativeQuery attribute to true.
The source code of our repository interface looks as follows:
import org.springframework.data.repository.Repository; import java.util.Optional; interface TodoRepository extends Repository<Todo, Long> { @Query(value = "SELECT * FROM todos t WHERE t.title = 'title'", nativeQuery=true ) public List<Todo> findByTitle(); }
Let’s move on and find out how we can create a query method that fulfils the requirements of our search function.
Implementing the Search Function
This section describes how we can implement a case insensitive search function that returns all todo entries whose title or description contains the given search term.
Implementing the Search Function With JPQL
We can create the JPQL query that fulfils the requirements of our search function by following these steps:
- Create a query method that returns a list of Todo objects.
- Annotate the method with the @Query annotation.
- Create the JPQL query which uses named parameters and returns todo entries whose title or description contains the given search term (remember to ignore case). Set the created query as the value of the @Query annotation.
- Add one method parameter to the query method and configure the name of the named parameter by annotating the method parameter with the @Param annotation.
The source code of our repository interface looks as follows:
import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; import java.util.List; interface TodoRepository extends Repository<Todo, Long> { @Query("SELECT t FROM Todo t WHERE " + "LOWER(t.title) LIKE LOWER(CONCAT('%',:searchTerm, '%')) OR " + "LOWER(t.description) LIKE LOWER(CONCAT('%',:searchTerm, '%'))") List<Todo> findBySearchTerm(@Param("searchTerm") String searchTerm); }
Let's find out how we can implement our search function by using SQL.
Implementing the Search Function With SQL
We can create the SQL query that fulfils the requirements of our search function by following these steps:
- Create a query method that returns a list of Todo objects.
- Annotate the method with the @Query annotation.
- Create the invoked SQL query by following these steps:
- Create the SQL query which uses named parameters and returns todo entries whose title or description contains the given search term (remember to ignore case). Set the created query as the value of the @Query annotation's value attribute.
- Set the value of the @Query annotation's nativeQuery attribute to true.
- Add one method parameter to the query method and configure the name of the named parameter by annotating the method parameter with the @Param annotation.
The source code of our repository interface looks as follows:
import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; import java.util.List; interface TodoRepository extends Repository<Todo, Long> { @Query(value = "SELECT * FROM todos t WHERE " + "LOWER(t.title) LIKE LOWER(CONCAT('%',:searchTerm, '%')) OR " + "LOWER(t.description) LIKE LOWER(CONCAT('%',:searchTerm, '%'))", nativeQuery = true ) List<Todo> findBySearchTermNative(@Param("searchTerm") String searchTerm); }
Let’s move on and find out when we should create our database queries with the @Query annotation.
When Should We Use the @Query Annotation?
The @Query annotation has the following benefits:
- It supports both JPQL and SQL.
- The invoked query is found above the query method. In other words, it is easy to find out what the query method does.
- There is no naming convention for query method names.
The @Query annotation has the following drawbacks:
- There is no support for dynamic queries.
- If we use SQL queries, we cannot change the used database without testing that our SQL queries work as expected.
When we think about the pros and cons of the @Query annotation, it becomes clear that the query methods that use this strategy aren’t as easy to write or read as the query methods that use the query generation from the method name strategy.
However, these query methods have two crucial advantages:
- If we need to find out what database query is invoked by our query method, we can find the invoked query above the query method.
- The code that uses our query methods is easy to read because we don’t have to use long method names.
Thus, if we don’t want to use the query generation from the method name strategy, because the names of our query methods would be too long, we should use the @Query annotation.
Let’s move on and summarize what we learned from this blog post.
Summary
This blog post has taught us three things:
- The database queries specified by using the @Query annotation precedes all other query generation strategies.
- The @Query annotation supports both JPQL and SQL queries.
- If we cannot create our database queries from the method names of our query methods, because the method names would be too long, we should create them by using the @Query annotation.
The next part of my Spring Data JPA tutorial describes how we can create database queries by using named queries.
P.S. You get can get the example application of this blog post from Github.
Thanks very іnteresting blog!
You are welcome!
This is the first tym m reading about "Spring Data JPA" and this blog has given me crystal clear idea about the topic.
Thanks :)
You are welcome! Also, thank you for your kind words. I really appreciate them.
Your tutorials have been very helpful. I've been following along with my own tables/entities, and I'm having an issue I haven't been able to solve at this point. My table is named poc_site, and my entity class is Site. I can't use any of these as queries:
I've been googling for how to determine the appropriate name to enter, and haven't found anything... how are these names determined here?
Thanks!
Actually, I figured out my issue...
Hi Marissa,
I am happy to hear that you where able to solve your problem. I assume that your problem was that the
site_name
is the name of the database column and not the name of the entity class' field. Am I correct?Hi Petri,
Thank You for the blog.
How we can give setfetchsize with @Query.
Thanks in advance
Hi Venkata,
You can use the
@QueryHints
annotation for this purpose. Check out this StackOverflow question. Its answer explains how you can set the fetch size.thanks a lot! this passage in the custom query gave me headaches before I read this: LIKE LOWER(CONCAT('%',:searchTerm, '%'))
Now its clear!
:)
You are welcome! I am happy to hear that this blog post was useful to you.
Hey Petri, your blog is awesome. It is being very useful to me and my projects.. I want to ask you if could you make a post about calling stored procedures that returns data (for example a list of articles) with JPA (Spring or Hibernate).
Thank you!
Hi Fernando,
Check out a blog post titled: How to call stored procedures in JPA.
Hi Petri!
Is there a way to eager load enities wher usin native queries?
Assume EntityA has a list of EntityB and a list of EntityC. What i want is:
@Query(value = "SELECT a.*, b.*, c.* " +
"FROM A a " +
"JOIN B b ON b.idA = a.id " +
"JOIN C c ON c.idA = a.id " +
"WHERE a.title = 'title'",
nativeQuery=true
)
public EntityA findByTitle();
i would like thies method to return the full graph(A's with its B's and C's), and not only A's, lazy loading B's and C's.
Hi,
I have to admit that I don't know if JPA supports this. However, it is possible to do this if you are using Hibernate. Unfortunately you have to use Hibernate specific API. If you want to do this, you should check out this blog post.
Hello,
I am having problems getting Page results for generic repository.
also
Interesting thing is if i use List everything works just fine.
Do you have any idea how to get around this issue?
Thanks.
Let me correct myself. It is not generic repository, rather another interface with custom methods.
public interface MyRepository extends jpaRepository,IRepositoryWithUsername
then
Works fine for one instance and also List but Slice and Page are returning empty result.
Hi,
Hi have a vague memory that I had a similar problem (although I used the
@Query
annotation). If I remember correctly, I solved it by specifying the count query. You can specify it by annotating your query method with the@Query
annotation and setting the count query as the value of the@Query
annotation'scountQuery
attribute.Let me know if this solved your problem.
Hi Dear,
Your link has helped me a lot but I have a question.
I want to write the native query in @Query annotation with where clause customized like select a.name from tableA a where [param1]=?1 and [param1]=?2
If i do not send a param1 value it should not restrict the query upon it.
Hi,
Unfortunately you cannot use that query with the
@Query
annotation. You have to use either JPA criteria API or Querydsl.Hi Dear,
I am using StringBuilder to use with createNamedQuery but on line javax.persistence.Query query = em.createNamedQuery(queryBuilder.toString()); it returns null pointer.
do not know what is causing the exception.
here is my code.
Project based on Spring boot Spring JPA repositories.
Thanks
I cannot find any obvious bugs from your code. If the
NullPointerException
is thrown from that line, eitherEntityManager
orStringBuilder
isnull
. I recommend that you use a debugger since it will tell you which object isnull
.Also, since you are writing a dynamic query, you should use JPA Criteria API or Querydsl. These tools will make your code cleaner (if you use Querydsl) and safer.
Dear Petri,
I have just implemented the JDBC through which run queries and returning the list.
See the chunk of code.
[
Connection con = getConnection();
ResultSet resultset = getStatement(con).executeQuery(queryBuilder.toString());
CustomerReportBean customerReportBean = new CustomerReportBean();
while (resultset.next()) {
customerReportBean = new CustomerReportBean();
if (resultset.getString(1) != null) {
customerReportBean.setDate(resultset.getString(1));
customerReportList.add(customerReportBean);
}
// Closed connection
con.close();
]
private Connection getConnection() {
DataSource ds = (DataSource) ApplicationContextProvider.getApplicationContext().getBean("dataSource");
Connection con = null;
try {
con = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
Kindly let me know is it a good practice or not, will it reduce the efficiency of the application.
Just i am doing for reporting purpose as i am not using any reporting tool.
Thanks
Hi,
Spring Framework provides an excellent support for invoking SQL queries. If you use the
JdbcTemplate
(orNamedParameterJdbcTemplate
) you don't have to write so much boilerplate code. This keeps your code a lot cleaner. In other words, use the components provided by Spring Framework (you won't be disappointed).✢ ✥ ✦ ✧ ❂ ❉ ✱ ✲ ✴ ✵ ✶ ✷ ✸ ❇ ✹ ✺ ✻ ✼ ❈ ✮ ✡
Dear Petri,
Thank you very much ...for helping...
have implemented JdbcTemplate. now my code looks a lot cleaner .
✢ ✥ ✦ ✧ ❂ ❉ ✱ ✲ ✴ ✵ ✶ ✷ ✸ ❇ ✹ ✺ ✻ ✼ ❈ ✮ ✡
You are welcome!
Thank you sir your query is perfectly work in my project
You are welcome!
I want to write jpa query to select all record in date range with some fileters, I am using example for that but its not working. JPA query written by me is as follows
public List findAllByOtActDateBetween(Example example,Date startDate,Date endDate);
please help me to correct it..
Hi,
Unfortunately I need a bit more information before I can try to figure out what is wrong. Can I see the relevant entities? Also, when you say that your query is not working, what do you mean? Does it return too many rows, wrong rows, or no rows at all?
Hi, i have a question... if we have combined methos in an interface, some with @Query and some without @Query how we implements this interface, if we only would like override the method that has not @Query (I know that an implements has to defining all methods)?
For example:
/***********INTERFACE********/
public interface UserRepository {
@Query(value = "{ 'username' : ?0}")
User findByUserName(String username);
List findByFilter(Map map); //without @Query because needs mor complex implementation
}
/***********IMPLEMENTATION********/
public class UserRepositoryImpl implements UserRepository{
@Override //I don't want override this, because already exist the query.. but i have to define new the method?.. or i have use other interface for methods that not use @Query annotation?
public User findByUserName(String username) {
return null;
}
@Override //I only need override this method
public User findByFilter(Map map) {
return null;
}
}
I hope you can help me.
Awesome article!...
Hi,
You have to create a new interface that declares your custom methods and implement that interface. I have written a blog post that describes how can add custom methods to a single Spring Data JPA repository. It should help you to solve your problem.
the response has already been committed. As a result, the response may have the wrong status code. Always getting this error...
@Entity
@NamedQuery( name = "Fields.findByName", query = "SELECT s FROM Fields s WHERE s.name = 'aseem'")
@Table(schema = "ASEEM", name = "com.aseem.ph.db::Table.FIELDS_NEW")
public interface FieldsDAO extends JpaRepository {
public List findByName();
}
Its solved, issue was in my return response..Thanks for the great article...
Hi,
I am happy to hear that you were able to solve your problem. Also, I am sorry that it took me so long before I answered to your comment. I am currently on summer holiday and my response times can be a bit slow.
how can we write Unit test for @Query annotation methods?
Hi,
You cannot write "pure" unit tests for these methods because they don't have an implementation when your unit tests are run. Also, you shouldn't write unit tests for data access code because unit tests won't help you to ensure that your data access code is working as expected. If you want to get more information about testing data access code, you should take a look at this tutorial.
Now, because Spring Data JPA creates implementations for your query methods when Spring loads the application context of your application, you can test your query methods by writing integration tests. If you need more information about writing tests for Spring or Spring Boot web applications, you should take a look at my testing course.
can i know how to add comment in @query annoation
as we use /* my comment*/ because this comment did not work for me in jpa
Hi,
If you want to document your database query, you can document the repository method by using the Javadoc syntax.
Actually i'm not rying to document it but i'm trying to identify the query using a label
SELECT /*+label(myselectquery)*/ COUNT(*) FROM t;
so if i use this label in my java jpa code
my database team can easily identify the query where it belongs to and can help in better performance
Hi,
That makes perfect sense. Unfortunately it seems that the
@Query
annotation (or JPQL) doesn't support labels. Actually, it seems that the only way to add comments to the generated SQL is to use the Hibernate Criteria API.Hi. Thanks for the informative article. How can i pass a null to date column which is nullable. I couldn’t find anything like setNull in Prepared statements. Thank you.
Hi,
If you are using JPA (either Spring Data JPA or EntityManager), you can simply set the value of the entity's field to
null
and persist (or update) the entity.Thanks for nice blog, Can we have subquery with Query block??
Yes. You can use a subquery as long as it's supported by JPQL or SQL.
love the content
Thank you for your kind words. I really appreciate them.
can we send a complete query string from my controller and execute it
No. You cannot do this with Spring Data JPA.
Thanks for sharing...good one
multiple select queries at a time executing how to avoid multiple queries
I want a code i,e. (if i want print only first it should display only with first name by using query annotation)
I done with code but im having some errrors
How to implement this interface in my service?
Hi, How to implement the Query Builder Tool with Search Filter using spring boot and jsp
Hi,
Before I can offer you any advice, I need to know the requirements of this query builder tool. Could you add them on this thread?
do you know Jira Query Language(JQL)
No. I try to stay as far away from Jira administration / customization as possible.
ok, Thanks
Hi Petri, In my local spring boot-microservices application all APIs are working but when I deploy the application to the dev server so the time application is working but on the dev server randomly 400 null
exception is coming how to fix this exception
Hi,
When the exception is thrown? Also, can you include the full stack trace when you answer to my comment?