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.
Related
I have a problem with modifying data while setting database replication
Before DB replication, I get data that I want to modify using repository.findById() and then I modified the data.
But I realized that repository.findById() is set #Transactional(readOnly = true) in SimpleJpaRepository.java so that I can't modify the data even if I use #Transactional in my Service or repository.save()
Is there any other way to force findById() to connect by a write connection except by making custom method in the repository for findById?
+++)
I solved my problem! I wanted to use dirty checking for modifying datas and I realized that my setting about EntityManagerFactory was something wrong and I fixed it with a doc in spring.io (https://docs.spring.io/spring-data/jpa/docs/current-SNAPSHOT/reference/html/#reference) I tried many times with many other developers posting but they didn't work for me, but it did. Thank you for giving me answers ðŸ˜
Refer this,
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
Section 5.7.1. Transactional query methods to be more specific
It says that #Modifying annotation can be used to override transaction configuration
Typically you will use the readOnly flag set to true as most of the query methods will be reading ones. In contrast to that deleteInactiveUsers() makes use of the #Modifying annotation and overrides the transaction configuration. Thus the method will be executed with readOnly flag set to false.
You don't need to change that flag!
Find the data
Edit data
Call JPA repository.save(newData) method with #Modifying to save the edited data in the DB
I.E.
#Transactional
#Modifying
#Query(value = "UPDATE user SET points = points + ?1
WHERE id = ?2", nativeQuery = true)
int increasePoints(int points, Long id);
I'm experiencing what seems to be a record lock between sequential (not concurrent) database operations, which I can't explain.
Situation
Method saveRegistrationToken is called from a REST controller. I test the calls to the method via Postman (HTTP client); the method is not called anywhere else, this is the only operation executed.
The method execution is supposed to behave as follows:
Take a String (registration token) and a user ID (also a string) as input
It should first update a USERS table, setting the value of the REGISTRATION_TOKEN column to null for every row where the column has the same value as the input registration token
It should then update the USERS table for the row with the specified user ID, setting the value of the REGISTRATION_TOKEN column to the input registration token.
Problem
Every first execution of the method will behave as expected: sets the value of the DB column REGISTRATION_TOKEN (table USER) to null wherever it is the specified value, and then sets the registration token to the input value for the row with the input user ID. As such, the value of the registration token for the row in question is the input value at the end of the execution of the method.
Every second execution will correctly do the first step ("void" the registration token wherever it exists) but does not update the value for the row with the specified user ID. As such, the value of the registration token for the row in question is null at the end of the execution of the method.
DefaultUserService.java
#Override
public void saveRegistrationToken(String userId, String registrationToken) {
usersRepository.voidRegistrationToken(registrationToken);
User u = usersRepository.findById(userId).get();
u.setRegistrationToken(registrationToken);
usersRepository.save(u);
}
UsersRepository.java
#Repository
public interface UsersRepository extends JpaRepository<User, String> {
#Modifying
#Transactional
#Query(value = "UPDATE USERS " +
"SET registration_token = null " +
"WHERE registration_token = :registration_token",
nativeQuery = true)
void voidRegistrationToken(#Param("registration_token") String registrationToken);
}
User.java
#Entity(name = "users")
#AllArgsConstructor //lombok
#Data
#NoArgsConstructor
#ToString
#EqualsAndHashCode
public class User {
#Id
private String id;
private String registrationToken;
private String email;
}
What I've tried
I initially thought it would be a flush problem: that once the registration token had been set to null everywhere, the transaction would not be flushed until after the registration token had been set again for the user ID, leading to conflicting behaviour between both DB operations. I disproved that explicitly calling usersRepository.flush(); after the first operation, and observing the same behaviour.
I tried different propagation and isolation levels on the repository operation: #Transactional(propagation = Propagation.SUPPORTS, isolation = Isolation.READ_UNCOMMITTED), which didn't help.
I tried explicitly setting the flush mode on the repository operation: #QueryHints(value = { #QueryHint(name = org.hibernate.annotations.QueryHints.FLUSH_MODE, value = "ALWAYS") }) , which didn't change anything.
It now seems to me that the first operation "locks" the updated record, which prevents the second operation from updating it, but I don't understand how.
Explicitly specifying auto-commit true: spring.datasource.auto-commit=true
Dependencies: compile("org.springframework.boot:spring-boot-starter-data-jpa") effectively version 2.1.1.RELEASE
Any ideas, explanations, links to docs would be very much appreciated - I've tried everything I can think of.
Many thanks, Chris
UPDATE:
Another reason I think it's some kind of flush problem.
I updated this method as follows:
#Override
public void saveRegistrationToken(String userId, String registrationToken) {
usersRepository.voidRegistrationToken(registrationToken);
String check = usersRepository.findById(userId).get().getRegistrationToken();
/* breakpoint on the following line */
User u = usersRepository.findById(userId).get();
u.setRegistrationToken(registrationToken);
usersRepository.save(u);
}
When stopping at the breakpoint where indicated:
Every first ("normal") execution, the value of the check variable is null
Every second execution, its value is the same as the input registration token
Although I always prefer to mark the service method as #Transactional as a whole, reviewing your code, I think you defined the appropriate transaction demarcation in your methods, by explicitly define the #Transactional annotation in voidRegistrationToken, and by using the methods provided by JpaRepository, implicitly annotated in such a way.
In any case, as you indicated, as a result of performing the different operations over the User who will be assigned the registration token, you are obtaining inconsistent values.
It is a clear indication that the information of the affected User entity maintained by the EntityManager in the persistence context is being polluted somewhere across the different methods invocation.
I honestly cannot give you the exact reason about this behavior.
It may have to do with the moment in which the changes are flushed to the database, to entirely discretion of the EntityManager, but you already tried to flush the different changes manually and your transactions seems appropriate and, as a consequence, it will probably not be the cause of the problem.
Maybe it have to do with a second level cache as #Guillaume suggested, maybe with the way the #Modifying operation is implemented by Spring Data.
One think you can try is to instruct your #Modifying annotation to clear the persistence context once the operation is completed:
#Modifying(clearAutomatically = true)
This will provide you a clean state to perform the registration token update.
Please, see the relevant docs.
Please, be aware that the possible implications of the use of this solution.
The use of flush and clear in EntityManager is an anti-pattern, something that should be avoided if you can by using the appropriate transaction demarcations and component architecture.
The call of clear leads to all objects being decoupled from the EntityManager. Be aware that, depending of the use case, even modified object data will not be saved to the database - this is the main difference with flush that always will preserve the modifications performed to the entities managed in the persistence context before detach them.
In any case, due to the way your transactions are defined, probably in your use case it will work properly.
I have a simple update query just to check if update query works:
this.openDBTransaction();
Query updateQuery = session.createQuery(
"UPDATE User AS usr SET usr.failedLogins=666 WHERE usr.id='USER_3000'"
);
int result = updateQuery.executeUpdate();
this.closeDBTransaction();
but somehow DB is not update with desired value. result came as 1 so something took place but for sure not update query.
Any clue what is going on?
You should use #Transactional annotation so that the compiler knows that the transaction is manipulating the database, thus permits to perform Data Manipulation queries or it will simply execute it as a Data Definition Language query.
Look at the code snippet below, for example,
#Transactional
public Employee editEmployee(Employee employee) { //employee is the data you got through post
return entityManager.merge(e1);
}
Also, the best practice is to always implement Data Access Object Interface and its implementation and define your queries in the implementation.
I hope this helps.
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
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.