Spring Boot: check if a given String is unique in the database - spring-boot

In Spring Boot, I need to check if a random String is unique. I suppose that a good way is to use an Entity with an unique column, and than:
if the String exists in the repository, adds it to the repository and return it.
That involses some code that may fail in a multithread environment, because while a thread is checking if a given String exists in the database, another thread can add it to the database at the same time.
Could you give me some hits to solve this problem? Thank you.

You can use pessimistic locking to solve that.
Spring has a #lock annotation with a lock type pessimistic, that might serve your needs. Otherwise you can implement your application in a way where it locks the entity before querying and releases the lock afterwards.
I would start from here: https://en.wikipedia.org/wiki/Record_locking

You can enable transaction locks on query Methods with #Lock annotation.
#Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Person> findById(Long PersonId);

Related

FindOne brings me the value from the cache memory and not the one from the database in Spring Data

I have a problem with spring data, when executing FindOne when I am updating a product, to compare the value of an attribute with the same one in the database. The FindOne process brings me the same object that I have in memory and not the one from the database, someone knows how I do to bring the one from the database, I know it's because of the hibernate cache, but I can't make it work in repository
#Override
public CuentaDetalle findOne(Long id) {
return cuentaDetalleRepository.getOne(id);
}
It can be done. You should detach the entity.
If everything is well set in your spring-boot project you can easily autowire EntityManager, and then later in your method, you can use entityManager.detach()
#Autowired
private EntityManager entityManager;
...
public someMethod(CuentaDetalle cuentaDetalleToDetach) {
entityManager.detach(cuentaDetalleToDetach);
Then later when using return cuentaDetalleRepository.getOne(id); where id is equal to the cuentaDetalleToDetach.id, the fresh veriosn from db will be returned.
Although I strongly advise using this approach carefully.
There are some drawbacks to detached objects, for example, you cannot use lazy fetch on collection properties (one-to-many).
Then the detached entity will not be saved at the end of the transaction. To save it you should explicitly use cuentaDetalleRepository.save(cuentaDetalleToDetach).
I will stop here, there is a lot to write on this topic.
But I hope, I did answer your question.

Spring Data problem - derived delete doesn't work

I have a spring boot application (based off spring-boot-starter-data-jpa. I have an absolute minimum of configuration going on, and only a single table and entity.
I'm using CrudRepository<Long, MyEntity> with a couple of findBy methods which all work. And I have a derived deleteBy method - which doesn't work. The signature is simply:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
Long deleteBySystemId(String systemId);
// findBy methods left out
}
The entity is simple, too:
#Entity #Table(name="MyEntityTable")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MyEntityPID")
private Long MyEntityPID;
#Column(name="SystemId")
private String systemId;
#Column(name="PersonIdentifier")
private String personIdentifier;
// Getters and setters here, also hashCode & equals.
}
The reason the deleteBy method isn't working is because it seems to only issue a "select" statement to the database, which selects all the MyEntity rows which has a SystemId with the value I specify. Using my mysql global log I have captured the actual, physical sql and issued it manually on the database, and verified that it returns a large number of rows.
So Spring, or rather Hibernate, is trying to select the rows it has to delete, but it never actually issues a DELETE FROM statement.
According to a note on Baeldung this select statement is normal, in the sense that Hibernate will first select all rows that it intends to delete, then issue delete statements for each of them.
Does anyone know why this derived deleteBy method would not be working? I have #TransactionManagementEnabled on my #Configuration, and the method calling is #Transactional. The mysql log shows that spring sets autocommit=0 so it seems like transactions are properly enabled.
I have worked around this issue by manually annotating the derived delete method this way:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
#Modifying
#Query("DELETE FROM MyEntity m where m.systemId=:systemId")
Long deleteBySystemId(#Param("systemId") String systemId);
// findBy methods left out
}
This works. Including transactions. But this just shouldn't have to be, I shouldn't need to add that Query annotation.
Here is a person who has the exact same problem as I do. However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
Oh, for reference I'm using Spring Boot 2.2.9.
tl;dr
It's all in the reference documentation. That's the way JPA works. (Me rubbing hands washing.)
Details
The two methods do two different things: Long deleteBySystemId(String systemId); loads the entity by the given constraints and ends up issuing EntityManager.delete(…) which the persistence provider is about to delay until transaction commits. I.e. code following that call is not guaranteed that the changes have already been synced to the database. That in turn is due to JPA allowing its implementations to actually do just that. Unfortunately that's nothing Spring Data can fix on top of that. (More rubbing, more washing, plus a bit of soap.)
The reference documentation justifies that behavior with the need for the EntityManager (again a JPA abstraction, not something Spring Data has anything to do with) to trigger lifecycle events like #PreDelete etc. which users expect to fire.
The second method declaring a modifying query manually is declaring a query to be executed in the database, which means that entity lifecycles do not fire as the entities do not get materialized upfront.
However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
There's detailed explanation why it works the way it works in the comments to the ticket. There are solutions provided even. Workarounds and suggestions to bring this up with the part of the stack that has control over this behavior. (Shuts faucet, reaches for a towel.)

Spring native query executed within a transaction taking outdated value

I'm using Spring Boot (1.4.4.REALEASE) with Spring Data in order to manage a MySql Database. I've got the following case:
We update one revision performed in one equipment using the RevisionService.
RevisionService saves the revision and calls the EquipmentService to update the equipment status.
The updateEquipmentStatus does a call to a Db stored procedure in order to evaluate the equipment with its revisions altogether and update the field.
I've tried some options but don't achieve to get the updated status for the equipment. The updateEquipmentStatus method keeps writing the previous status for the equipment (not considering the current revision being stored in the transaction). The code is written this way:
RevisionService
#Service
public class RevisionService{
#org.springframework.transaction.annotation.Transactional
public Long saveRevision(Revision rev){
//save the revision using JPA-Hibernate
repo.save(rev);
equipmentService.updateEquipmentStatus(idEquipment);
}
}
EquipmentService
#Service
public class EquipmentService{
#org.springframework.transaction.annotation.Transactional
public Long updateEquipmentStatus(Long idEquipment){
repo.updateEquipmentStatus(idEquipment);
}
}
EquipmentRepo
#Repository
public interface EquipmentRepo extends CrudRepository<Equipment, Long> {
#Modifying
#Procedure(name = "pupdate_equipment_status")
void updateEquipmentStatus(#Param("id_param") Long idEquipment);
}
As far as I understand, as both methods are annotated with Spring's transactional, the updateEquipmentStatus method should be executed in the scope of the current transaction. I've also tried with different options for the #Transactional annotation from updateEquipmentStatus, such as #Transactional(isolation=Isolation.READ_UNCOMMITTED) (which shouldn't be required, because I'm using the same transaction) and #Transactional(propagation=Propagation.REQUIRES_NEW), but keeps not considering the current status. That's how my stored procedure is saved into the MySql DB:
CREATE DEFINER=`root`#`localhost` PROCEDURE `pupdate_equipment_status`(IN `id_param` INT)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
BEGIN
/*Performs the update considering tequipment and trevision*/
/*to calculate the equipment status, no transaction is managed here*/
END
I also want to clarify that if I execute some modification in the equipment itself (which affects only tequipment), the status is being properly updated. InnoDb is the engine being used for all the tables.
UPDATE
Just changed the repo method to use a nativeQuery instead and the same problem keeps happening, so the Db procedure being involved should be discarded:
#Modifying
#Query(nativeQuery = true, value= "update tequipment set equipment_status = (CASE WHEN (...))")
void updateEquipmentStatus(#Param("id_param") Long idEquipment);
UPDATE2
Having done more tests and added a log with TransactionSynchronizationManager.getCurrentTransactionName() in the methods, that's the concrete issue:
Changes done in the equipment service are properly picked by the updating function (When something in tequipment changes, the status in tequipment is calculated properly).
Changes done in the revision service (trevision) result in an outdated value in tequipment (it doesn't matter if Spring does it in a different transaction using REQUIRES_NEW or not). Spring seems to create a new transaction properly when using REQUIRES_NEW in establishEquipmentStatus, because the current transaction name changes, but the native query doesn't have the latest values (because of the transaction before not being commited?). Also tried removing #Transactional from establishEquipmentStatus so the same transaction is used, but the issue keeps happening.
I would like to highlight that the query used to update equipment status has a case expression with multiple subqueries using trevision.
Adding the following code fixes it (programatically flushing the transaction state to the Database):
#Service
public class EquipmentService{
#PersistenceContext
private EntityManager entityManager;
#org.springframework.transaction.annotation.Transactional
public Long updateEquipmentStatus(Long idEquipment){
entityManager.flush();
repo.updateEquipmentStatus(idEquipment);
}
}
Still it would be great to find a declarative way to do it..
Changing to read uncommitted is the right idea but you'd also need to flush the entitymanager before your stored procedure is called. See this thread:
How to make the queries in a stored procedure aware of the Spring Transaction?
Personally I'd do it all in Spring unless you are absolutely forced to use a stored procedure.

finall() in Spring services

Yesterday I've got access to the new project in my company and I have found this
public List<User> findNotActiveUsers() {
return this.userRepository.findAll().splititerator()
.filter(u -> u.isActive())
.collect(Collect.toList());
}
Is this a good way to find all the active users? Or should it be done in a repository like this?
public interface UserRepository extends JpaRepository<Long, User> {
#Query("SELECT user FROM User user WHERE user.active IS TRUE")
List<User> findActiveUsers();
}
And If first solution is correct what about performance?
Firstly, both options fulfill the requirement.
However, the option 2 makes more sense to filter the data at query level rather than at Java level. I believe the performance would be better on the second option though I don't have any data to backup this statement. I have commented about the performance based on my experience.
You can also consider whether Cache (#Cacheable) can be used. It purely depends on the use case i.e. how frequently the User entity is changed and how frequently you would like to refresh the cache.
One disadvantage of using native query is that currently Spring JPA doesn't support execution of dynamic sorting for native queries.
Please refer the similar question discussed in the below link though it is very much related to Hibernate. Clearly, the option 3 is preferred (i.e. #Query approach).
Spring Data Repository with ORM, EntityManager, #Query, what is the most elegant way to deal with custom SQL queries?

#Transactional Annotation + for a data insertion in a loop

I am using Spring 3, JPA + Hibernate for a CMS application. In that application I have a service class method which is annotated with #Transactional Annotation with rollBack property. Inside that method I am inserting data (ie entity classes) to a table using a loop. For each iteration of the loop entity classes has to be saved to the database. But it is not happening. The commit only happens when the execution of the loop has completed and exits from the method. Then it commits and saves all at once. But I need to read data once it gets inserted into the database before committing in this case. I tried with the ISOLATION LEVEL to read uncommitted but it didn't supported since I am using the default JPADialect. Also tried to add the hibernate implementation of jpaDialect but still it didn't worked. Please help with a workaround for this problem. One more thing, is there any way using propagation required method.
You are right, this is what I stands for in acid. Because the transactions are working in isolation, other transactions cannot see them before they are committed. But playing with isolation levels is a bad practice. I would rather advice you to run each and every iteration in a separate transaction with start and commit inside.
This is a bit tricky in Spring, but here is an example:
public void batch() {
for(...) {
insert(...)
}
}
//necessarily in a different class!
#Transactional
public void insert() {
}
Note that batch() is not annotated with #Transactional and insert() has to be in a different class (Spring service). Too long to comment, but that's life. If you don't like it, you can use TransactionTemplate manually.
remove the transactional annoation on the the method with loop.
In the loop call a separate method to perform the save, make that method transactional
You either need to go with programmatic transactions (Spring's TransactionTemplate or PlatformTransactionManager are the classes to look at, see Spring Doc for programmatic transactions, or you can call another transactional method from within your loop where the transaction is marked with Propagation.REQUIRES_NEW, meaning each call of that method is executed in its own transaction, see here. I think that the second approach requires you to define the REQUIRES_NEW method on a different Spring bean because of the AOP-Proxy. You can also omit the REQUIRES_NEW if the loop is not executed within a transaction.

Resources