I just got started with Spring JPA and I am reviewing code written by my coworker.
I see that he has been using the following code to find a Login object using username:
public interface LoginDao extends JpaRepository<Login, Long> {
#Query("SELECT u FROM Login u WHERE LOWER(u.username) = LOWER(:username)")
Login findByUsername(#Param("username") String username);
}
Cant he just create a method like this:
#GET
#Path("{username}")
public Login getOne(#PathParam("username") String username) {
Login login = loginDao.findOne(username);
if (login == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
} else {
return login;
}
}
What are the fundamental advantages of using #Query rather than writing a method approach. Or am I plain wrong and both have different purposes.
I personally dont want to write queries inside the code. I think mixing java with sql queries can make code look uglier.
Our stack is java, JPA, Jersey, JAX-RS, Postgreql, Spring Boot, Hibernate
Regards
First, This is not an SQL query, this is a JPQL one. It would be a native SQL query if the nativeQuery attribute was set to true, which is not the case by default.
It is perfectly fine to create queries with JPQL, you will be able to switch from Hibernate to Eclipse Link or another JPA implementation without issues. You should also be able to switch from Postgres to MySQL, SQL Server...
You have to start to worry if your coworker creates queries with #Query(nativeQuery = true, value = "..."), otherwise it looks fine to me.
Second, when you look to your repository declaration, you can see that the ID field for your Login entity is of type Long. That means the loginDao.findOne(..) method wants a Long parameter. Probably a surrogate key which is not username.
This is why your coworker created this new method: to find a Login row by something else than the Long surrogate key. (Here by username which is most likely a business key, but I do not know your application.)
Just for your information: Spring automatically creates queries with the signature of your repository methods (derived queries). For example:
public interface LoginDao extends JpaRepository<Login, Long> {
Login findByUsername(String username);
}
Spring JPA will automatically create a query looking for a field named username and create a WHERE clause with it.
Notice that it is not the same than your coworker query because it will not use the LOWER function.
Some anchors in the JPA documentation about those derived queries:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
Another thing about your title:
"Why need #Query when I can write a method in Spring JPA"
Well this #Query writes a method in Spring JPA. :^)
JPARepository has come up with some of the Supported keywords which will write the queries themselves based on your entity.
If we are looking something out of box from what JPARepository provides #Query is useful like - Joining multiple queries, writing native queries etc.
From your code snippet both would do the same
For more info you can refer - https://docs.spring.io/spring-data/jpa/docs/1.4.2.RELEASE/reference/html/jpa.repositories.html
Related
I am working on a desktop application built using spring framework and one of the part of the application is not working. I found that the repository class does not have any queries with #Query annotation. I haven't encountered it before.
When I try to open the form that uses this, I get an error that the application is not able to connect to the database. The application has 3 databases specified in the application.properties. I have the following questions:
1) How does the following code work without a query specified with #Query annotation. Or where is the query written.
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
List<Account> findAccountsByActiveIsTrueAndAccountTypeEquals(String accountType);
List<Account> findAccountsByAccountTypeLike(String type);
}
2) How do we specify which of the database to search for. For example: I have 3 mysql databases currently connected to my application. I wish to access data from DB1 through my Spring boot application through the usual flow of
UI model-> BE Controller/ Service layer -> Repository(Interface) which (usually) has the query written with #Query. How we specify which database this query goes for ?
For your first question I can answer that the JpaRepository has an internal system that analyses the method name you have written and then generates the query that has to be executed to the database.
The #Query annotation is used when the method name and the generated query is not returning the result you wanted to so you specifically tell the compiler which query should be executed.
As mentioned here: https://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html
2.3.1 Query lookup strategies.
The JPA module supports defining a query manually as String or have it being derived from the method name.
Declared queries
Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see Section 2.3.3, “Using JPA NamedQueries” for more information) or rather annotate your query method with #Query (see Section 2.3.4, “Using #Query” for details).
So basically using a naming convention will do the magic.
Also an interesting question and perfect answer can be found here:
How are Spring Data repositories actually implemented?
For your second question you can refer to this example:
https://www.baeldung.com/spring-data-jpa-multiple-databases
It might be a bit complicated in the beginning but eventually it will work.
He use JPA, JpaRepository has CRUD methodes
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference
In your application.properties, you can put your mysql DB info
Why this works without #Query?
Because you are using JpaRepository which provides an easy way to get data based on your entity and it's fields.
Here your Account will have active, accountType etc fields. You can use JPA's query creation keywords such as AND, OR, Equals, Like and many more.
Derived queries with the predicates IsStartingWith, StartingWith, StartsWith, IsEndingWith", EndingWith, EndsWith, IsNotContaining, NotContaining, NotContains, IsContaining, Containing, Contains the respective arguments for these queries will get sanitized. This means if the arguments actually contain characters recognized by LIKE as wildcards these will get escaped so they match only as literals. The escape character used can be configured by setting the escapeCharacter of the #EnableJpaRepositories annotation.
How do we specify which of the database to search?
You can create configuration classes based on your databases and define data sources based on that using #PropertySource.
For more details see example here
#Configuration
#PropertySource({ "classpath:persistence-multiple-db.properties" })
#EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
I'm new in the world of spring and Spring Data.
I've build a user- and permission management system where a user can grant and remove permissions for another user.
I've digged in the docs but I'm not shure how to handle querys where I just want to insert or manipulate Data in a Table.
In the docs query return type table I couldn't find something like a status or a status reporting object.
What I'm looking for is a pattern that allows me to controle is an operation was successfull. Right now I'm using primitives. When everything worked out I get "1" returned but when I query an empty table I get "0". So I need something with more details.
Thank you for guidance and sharing your experience.
EDIT:
Here is a code example:
#Transactional
#Modifying
#Query(value = "DELETE FROM permissions WHERE producerId=:producerId AND comsumerId=:consumerId", nativeQuery = true)
void clearAllPermissions(#Param("producerId") Long producerId,#Param("consumerId") Long consumerId);
The Method is provided by my repository class.
#Repository
public interface PermissionsRepository extends JpaRepository<ProducerConsumerPermissions, Integer>{
.
.
.
}
I call the class from my service layer which is used by my Controller layer.
I guess it'd be nice to know if this operation was successfull so that I can transport the exeption throught the layer till my frontend and throw a message to the user. But when the Table is empty I get a value of false back when I use Integer as return type.
Make your method return int then you know if any records are deleted.
#Transactional
#Modifying
#Query(value = "DELETE FROM permissions WHERE producerId=:producerId AND comsumerId=:consumerId", nativeQuery = true)
int clearAllPermissions(#Param("producerId") Long producerId,#Param("consumerId") Long consumerId);
If there is a problem it will throw and exception anyway. Spring Data throws DataAccessException that you can catch to see what went wrong.
I am new to Java and started with Spring Boot and Spring Data JPA, so I know 2 ways on how to fetch data:
by Repository layer, with Literal method naming: FindOneByCity(String city);
by custom repo, with #Query annotation: #Query('select * from table where city like ?');
Both ways are statical designed.
How should I do to get data of a query that I have to build at run time?
What I am trying to achieve is the possibility to create dynamic reports without touching the code. A table would have records of reports with names and SQl queries with default parameters like begin_date, end_date etc, but with a variety of bodies. Example:
"Sales report by payment method" | select * from sales where met_pay = %pay_method% and date is between %begin_date% and %end_date%;
The Criteria API is mainly designed for that.
It provides an alternative way to define JPA queries.
With it you could build dynamic queries according to data provided at runtime.
To use it, you will need to create a custom repository implementation ant not only an interface.
You will indeed need to inject an EntityManager to create needed objects to create and execute the CriteriaQuery.
You will of course have to write boiler plate code to build the query and execute it.
This section explains how to create a custom repository with Spring Boot.
About your edit :
What I am trying to achieve is the possibility to create dynamic
reports without touching the code. A table would have records of
reports with names and SQl queries with default parameters like
begin_date, end_date etc, but with a variety of bodies.
If the queries are written at the hand in a plain text file, Criteria will not be the best choice as JPQL/SQL query and Criteria query are really not written in the same way.
In the Java code, mapping the JPQL/SQL queries defined in a plain text file to a Map<String, String> structure would be more adapted.
But I have some doubts on the feasibility of what you want to do.
Queries may have specific parameters, for some cases, you would not other choice than modifying the code. Specificities in parameters will do query maintainability very hard and error prone. Personally, I would implement the need by allowing the client to select for each field if a condition should be applied.
Then from the implementation side, I would use this user information to build my CriteriaQuery.
And there Criteria will do an excellent job : less code duplication, more adaptability for the query building and in addition more type-checks at compile type.
Spring-data repositories use EntityManager beneath. Repository classes are just another layer for the user not to worry about the details. But if a user wants to get his hands dirty, then of course spring wouldn't mind.
That is when you can use EntityManager directly.
Let us assume you have a Repository Class like AbcRepository
interface AbcRepository extends JpaRepository<Abc, String> {
}
You can create a custom repository like
interface CustomizedAbcRepository {
void someCustomMethod(User user);
}
The implementation class looks like
class CustomizedAbcRepositoryImpl implements CustomizedAbcRepository {
#Autowired
EntityManager entityManager;
public void someCustomMethod(User user) {
// You can build your custom query using Criteria or Criteria Builder
// and then use that in entityManager methods
}
}
Just a word of caution, the naming of the Customized interface and Customized implementating class is very important
In last versions of Spring Data was added ability to use JPA Criteria API. For more information see blog post https://jverhoelen.github.io/spring-data-queries-jpa-criteria-api/ .
I was wondering if there was a way using JPA query (not sure what the word I'm looking for is sorry).
#Transactional
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastNameIgnoreCase(String lastName); //This is the format I am looking for
#Query("SELECT DISTINCT t.lastName FROM User t") //Don't want to have to use the #Query
List<String> findDistinctLastNames();
}
Hopefully that makes it more clear. But I am trying to perform that Query without having to use the #Query. It doesn't really affect anything having it there, I would just like it. Is that statement possible?
Spring Data JPA uses reflection to match method signatures to field names and operations so confirming you have the correct column name would be a good place to start, i.e. is it lastName or lastNames?
Otherwise, according to the Spring Data JPA documentation, the following should work correctly:
List<User> findDistinctByLastName(String lastName);
Are you getting a stack trace?
What is the right way do validate a jpa query programmatically. Hibernate validates all annotation based named queries on entities. But how can I call this validation routine on programmatically builded jpa queries, to check for errors?
#Entity
public class Foo {
#Id
public int id;
public String name;
}
main(...) {
Query q = getEntityManager().createQuery("select e from " + Foo.class.getName() + " e where e.name = 'x' ");
// validate q here
}
Don't. Integration test the hell out of your code, using a real database with the same schema as your production environment.
Think about it: if you create a malformed query, that's a programming bug. What are you going to do with the information? Tell the user that a JPA query is malformed? All you can realistically do is log the error and tell the user "something bad happened". You'll know it's a malformed query when you check the logs later anyway...
Edit
It might also be worth investigating the createQuery() call by feeding it bad data - the javadoc here suggests that it can throw a HibernateException, and I'm not sure what it could do with a string other than validate it...
Either you can use createQuery, or you need to put your class name while writing a JPQL.