Spring Data JPA: Ultimate Guide to Custom Queries with @Query Annotation

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hi, I’m Thorben Janssen from thoughts-on-java.org with another video. In this video, I will show you how to define and use custom queries with Spring Data JPA. You will learn how to execute JPQL and native queries, use an expression language in your queries and execute queries that INSERT, UPDATE, or REMOVE records. But before we start, if this is your first time here, and you want to learn how to create your entity mappings with ease, build incredible efficient persistence layers with Hibernate and all types of other Java persistence related stuff, start now by subscribing and clicking the bell, so you don't miss anything. Derived queries are very comfortable to use as long as the queries are not too complicated. But as soon as you use more than 2-3 query parameters or need to define multiple joins to other entities, you need a more flexible approach. In these situations, you better use Spring Data JPA’s @Query annotation to specify a custom JPQL or native SQL query. The @Query annotation gives you full flexibility over the executed statement, and your message name doesn’t need to follow any conventions. The only thing you need to do is to define a method in your repository interface, annotate it with @Query, and provide the statement that you want to execute. Spring Data JPA provides the required JPA code to execute the statement as a JPQL or native SQL query. Your preferred JPA implementation, e.g., Hibernate or EclipseLink, will then execute the query and map the result. Let’s take a more detailed look at the two query options. Most developers prefer JPQL queries because they allow you to define your query based on your domain model. Another advantage is that your JPA implementation handles the different database dialects so that the JPQL query is database agnostic. But these queries also have a downside. JPQL only supports a subset of the SQL standard. So, it’s not a great fit for complex queries. When you define a JPQL query in your repository definition, Spring Data JPA only provides the required JPA code to execute that query. The query itself gets processed and executed by your JPA implementation. So, you can still use everything you learned about JPQL queries with Hibernate or EclipseLink with Spring Data JPA. It just gets a little bit easier. In the following parts of the video, I will focus on the Query annotation and expect that you are already familiar with JPQL. If that’s not the case, please take a look at my free Ultimate Guide to JPQL Queries or signup for my JPA for Beginners course. Creating a JPQL query with Spring Data JPA’s Query annotation is pretty straightforward. You need to annotate a method on your repository interface with the Query annotation and provide a String with the JPQL query statement. Here you can see an example containing 2 queries that select Author entities by their firstName or by their firstName and lastName. Please ignore the method and query parameters for now. We will take a closer look at them in a later part of this video. As you can see here, both methods return a List of Author entities. But their SELECT clauses look very different. The findByFirstName method doesn’t define any SELECT clause, and the findByFirstNameAndLastName method uses the Author entity to specify the projection. If you take a look at the executed SQL statements, you can see that Hibernate, my preferred JPA implementation, generates the same SELECT clause for both queries. The SELECT clause of the findByFirstName query gets automatically generated so that it selects all columns mapped by the entities referenced in the FROM clause. In this example, these are all columns mapped by the Author entity. Based on this approach, you can define and execute any JPQL query that you might need for your use cases. So, feel free to give it a try and to write your own queries. For some use cases, you might want to retrieve the query result in a specific order. Using Spring Data JPA, you can define the sorting of your query results in 2 ways: You can add an ORDER BY clause to your JPQL query or You can add a parameter of type Sort to your method signature. You’re probably familiar with the first option. The ORDER BY clause is defined in the JPA standard, and it is very similar to the ORDER BY clause you know from SQL. You simply reference one or more entity attributes and use ASC or DESC to specify if you want to sort them in ascending or descending order. I explained the ORDER BY clause and all other parts of a JPQL query in more details my guide to JPQL. With Spring Data JPA, you can also add a parameter of type Sort to your method definition. Spring Data JPA will then generate the required ORDER BY clause. That is the same approach as you can use in a derived query. When you want to call the findAuthors method, you need to provide a Sort object. Its constructor expects an enumeration that defines the sorting direction and one or more Strings that reference the entity attributes, which you want to use to order the result, by their name. Independent of the approach you choose to define the sorting of your query result, your JPA implementation will add an ORDER BY clause to the generated SQL query. In addition to sorting, Spring Data JPA also provides very comfortable support for pagination. If you add a method parameter of type Pageable to your repository method, Spring Data JPA generates the required code to handle the pagination of the query result. When you call the findByFirstName method, you need to provide an object that implements the Pageable interface. You can do that by calling the of method on the PageRequest method with the number of the page and the size of each page as parameters. Spring Data JPA then calculates the required limit and offset values based on the provided page number and size and generates the JPQL query. For this JPQL query, your JPA implementation, for example Hibernate, then creates a SQL query and sends it to the database. In addition to the previously discussed query features, Spring Data JPA also supports SpEL expressions within your query. You can use it to avoid hard references to the name of an entity or to create advanced like expressions. In the previous queries, I always referenced entities by their name. That prevents you from defining queries for generic repositories. It also causes a lot of work if you decide to rename an entity because you then need to update all queries that reference the old name of the entity. You can avoid that by using the entityName variable in an expression. I do that in the following query to avoid referencing the Author entity by its name. Spring Data JPA replaces the entityName expression with the entityName of the domain type of the repository. So, in this example, Spring Data JPA replaces entityName with Author. Another great feature that you get with the SpEL expressions is the definition of advanced like expressions. You can, for example, append percentage sign to the beginning and end of a parameter and change the provided bind parameter value to upper case. As you can see in the log statements, Spring Data JPA called the toUpperCase method on the value of the method parameter firstName and added a percentage sign to the beginning and end of it. In the next step, it set the modified String as a bind parameter value. You can also use the @Query annotation to execute a native SQL query. As explained earlier, Spring Data JPA provides the required JPA code to execute the query. The query itself still gets handled by Hibernate or EclipseLink. So, you can use all your experience with these frameworks and read articles about them to get a deeper understanding of the JPA standard. Next, I will focus on Spring Data JPA’s support for native queries. If you want to learn more about native queries in general, please read my article Native Queries - How to call native SQL queries with JPA & Hibernate. Similar to the custom JPQL query, you can use the Query annotation to specify a custom native SQL query. But you need to tell Spring Data JPA, that you are defining a native query, so that it can execute the query in the right way. You can do that by setting the nativeQuery attribute of the Query annotation to true. Here you can see an example of a repository method that uses a native SQL query to load all Author entities with a give firstName. The query that you provide as the value of the annotation will not be parsed or changed by your JPA implementation. It gets directly send to your database. So, you need to make sure that you provide a valid SQL statement and follow the specific dialect of your database. To use both kinds of queries with Spring Data JPA, you need to be familiar with bind parameters. Bind parameters act as placeholders in your query for which you can set values before the query gets executes. They are regularly used in the WHERE clause of a query. You should always prefer them over inlining a parameter value because bind parameters: prevent SQL injection vulnerabilities, get automatically converted to the correct SQL type and enable your persistence provider and database to optimize your query. Spring Data JPA handles the bind parameters of JPQL and native SQL queries in the same way. For each query, you can choose if you want to use positional or named bind parameters. A positional bind parameter gets referenced by its position. In the query, positional bind parameters consist of a question mark followed by a number that specifies the position. Spring Data JPA then provides the required code to set the bind parameter values. It sets the value of each method parameter as the value of a bind parameter with the same position. So, the value of the first method parameter gets set as the value of the bind parameter at position 1, value of the second method parameter as the value of the bind parameter at position 2 and so on. Named bind parameters specify a name for each parameter. In your query, named bind parameters start with a column followed by the name of the parameter. That makes them much more expressive and easier to reference. Spring Data JPA can provide the required code to set the values of these bind parameters. But it needs a little bit of help to match the method parameters with the bind parameters in your query. You either need to annotate the method parameter with a Param and specify the name of the parameter, or you need to compile your application with Java 8 or higher and set the compiler flag -parameters. Here you can see an example of both bind parameter types. As you can see in the code snippets, named bind parameters are much easier to read and are not as likely to cause problems during refactoring. You should, therefore, prefer named bind parameters in your queries. You can not only use the Query annotation to define queries that read data from your database. You can also create queries that add, change, or remove records in your database. That enables you to implement bulk update or remove operations which can provide substantial performance improvements. These queries require a slightly different JPA code. That’s why you need to tell Spring Data JPA that your query modifies data by annotating your repository method with an additional Modifying annotation. It will then execute the provide JPQL or native query as an update operation. I use this approach here, to specify a query that adds a provided prefix to the firstName of all Authors. Spring Data JPA provides multiple options to define a query on your repository interface. You can use derived queries for which Spring Data JPA generates the required query based on the name of the repository method. I explained that in great details in a previous tutorial. or you can use the Query annotation to define your own query. That’s what we discussed in this video or you can reference a named query which you specified on one of your entities. We will take a look at that in a future tutorial. By using the @Query annotation, you get full control over the executed query. You can choose between a native SQL or a JPQL query. By default, Spring Data JPA expects that you provide a JPQL query. If you want to execute a native query, you need to set the nativeQuery parameter of the @Query annotation to true. Both kinds of queries get processed by the underlying JPA implementation. That enables you to reuse all your Hibernate and EclipseLink knowledge and to use the query features supported by these frameworks. You can also specify modifying queries which insert, update, or remove records from the database. Spring Data JPA needs to generate a slightly different JPA code for these. You, therefore, need to annotate repository methods that execute modifying queries with the Modifying annotation. OK, that’s it for today. If you want to learn more about Hibernate, you should join the free Thoughts on Java Library. It gives you free access to a lot of member-only content like a cheat for this video and an ebook about using native queries with JPA and Hibernate. I’ll add the link to it to the video description below. And if you like today’s video, please give it a thumbs up and subscribe below. Bye
Info
Channel: Thorben Janssen
Views: 36,469
Rating: 4.9504814 out of 5
Keywords: Hibernate, JPA, How to, Tutorial, Beginners, Java, Getting Started, JPQL, mapping, map, BLOBs and CLOBs, JDBC, OpenJPA
Id: 2SV7QODVHAE
Channel Id: undefined
Length: 15min 9sec (909 seconds)
Published: Wed Sep 25 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.