Understanding transactions in Spring Test - spring

I have a little bit trouble with Integration Test and the transactions.
I have a Rest Service System. Behind all I have a JPA-Repository, with a Postgres database. Now to test them I build JunitTest where I made the calls on the System. The test loads the web-context and an other xy-context where I have the configuration of security and database connections. On the test method I have the #Transactional annotation.
The test makes 2 requests (This is only one example I have more of similar scenarios on other Object):
insert a new user
on this user create a Group and after bind this to the user
The test makes the first call, and returns me a id where I use to perform the second call.
The second call take the id and make the post and there I have several problems.
Details of the second call:
Test make a post on a controller
Controller takes the request and forward it to the Service
Service method (with #Transactional) take the request and do:
a research to find the inserted user
insert a group object
update the user with the groupId (generated on point 2)
Now one of the problems I had, it was a AccessDeniedException on point 3.1, because I have also ACL, and I have to check if there are enough permissions.
One of the things that I tried to do is to set:
#Transactional(propagation=Propagation.REQUIRES_NEW)
on the Service Method.
What I get after is the result that the AccessDeniedException was disappeared but the research at 3.1 gives me empty result (the research is ok, because on other scenario I have correct results), but is strange because the first post was ok, and how I understand Spring handles the #Transactions and "commits" to database so that a commit is performed when a transaction is closed. This brings me to an other idea to try: remove the #Transaction annotation on the test, but when i made this, then the database has all the data of this scenario until the end of the tests session (If you have a lot of test this is not desirable), and this is not a very good thing.
Now I wrote a little bit where are my doubts, and problems without posting a lot of code and of privacy problems, but on request I can post little pieces of codes.
It's also probable that the approach is incorrect.
The questions are:
-how can I make this service work?
-It's the correct way to set (propagation=Propagation.REQUIRES_NEW)?
-It's the correct way to set #Transactional on the test? (eventually with a Rollback?)
Txs a lot.
To make test I use mockMvc to make the request and some annotation on the class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = { ..... })
#Transactional
public class tests {
#Test
public void aTest(){
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.addFilter(new DelegatingFilterProxy("springSecurityFilterChain", webApplicationContext), "/*")
.build();
mockMvc.perform(post(.....))
}
}

To answer your question:
It's the correct way to set #Transactional on the test? (eventually with a Rollback?)
No really, but you can. Because you are doing two requests, the second depends on the first, and http request will not remember your transaction, if you insist to do it, you need flush your session between requests.
It's the correct way to set (propagation=Propagation.REQUIRES_NEW)?
It depends. REQUIRES_NEW means it will start new transaction, the influence is that everything in the existing transaction will be invisible in the new transaction, because the old one is not commited yet! if this server is the entry point of the transaction, it makes no difference, but be aware of the visibility problem.
how can I make this service work?
OK, forget what my answers of the previous questions. If I have to write the test, I will do it this way:
The test is not transactional. If you are doing integration test, you don't need to rollback single tests. If you wanna rollback the commit, then you are having wrong task case, you should have two test cases insert user and update group.
3 parts of the test
Send request to insert user and get the ID (single transaction)
Send request to update group(another transaction)
Send request to fetch the user and do the checks.
Hope this can help you.

Related

Is it possible to make some action in a new Spring trascation before a user stars work with it?

I have Spring Boot JPA application.
An application user asks a new transaction from time to time.
I'd like to execute some action on a db connection that is related with the transaction just BEFORE the use can work with that transaction.
"Just BEFORE" means the action has been completed before the first statement of a method annotated with #Transactional is executed.
The action is an execution of a stored procedure with params depend on the current application user.
Application uses work with DB under a technical account.
Thank you in advance
First solution:
Put all in #Transacional annotated method, where your pre-transaction, however this may sound weird, part goes first.
This first part may perform any action and maybe throw en exception if anything goes wrong, thus forcing the transaction to roll back. This may be a #Repository method, a #Query annotated one if needed (like Josh Pospisil suggests) or whatever.
Second solution (I would prefer):
Put 3 method calls in your #RestController's method: first - determining the user, second - performing your pre-transactional action using this user's data, third - the actual #Transacional method doing the job.

Transaction issue when DB Call happens after Rest Call

I am using Spring Boot and my application is just Monolithic for now, may switch to microservices later.
SCENARIO 1: Here My DB call Does NOT depend on REST Response
#Transactional
class MyService {
public void DBCallNotDependsOnRESTResponse(){
//DB Call
//REST Call, This restcall gives response like 200 "successfull"
}
}
SCENARIO 2: Here My DB call depends on REST Response
#Transactional
class MyService {
public void DBCallDependsOnRESTResponse(){
//REST Call, making a Real Transaction using BrainTree
//DB Call, HERE DB CALL DEPENDS ON REST RESPONSE
}
}
In case of Scenario 1, I have no issues as DB gets rolled back incase REST fails.
BUT, incase of Scenario 2, REST call cannot be rolled back, incase if any exception occurs at DB call.
I already searched in google for above, I found some solutions like we need to use something like Pub-Sub model system seems, BUT I could not able to get that concept to my head clearly.
I will be glad if someone could able to provide solution for SCENARIO 2. How Other Ecommerce businesses handling their transactions effectively, I guess my query related to some Architecture design.. Please advice some good architecture approach to solve above Transaction issue. Do you think using some Messaging system like Kafka will solve above issue..? FYI, currently, my application is Monolithic, shall I use Microservices? Do I need to use two-phase-commit or Sagas will solve my problem? Does Sagas can be used for Monolithic application?
EDIT:
Regarding RestCall: I am actually making a Real Transaction using BrainTree, which is a Rest Call.
Can you elaborate what are you achieving from rest call? Are you updating any data that will be used by the DB call?
If the 2 calls are independent, will the order be of importance? Since db call will be committed at the end of method itself

Perform JSR 303 validation in transactional service call

I am using the play framework to develop a web application which accesses a postgres db using JOOQ and spring transactions.
Currently I am implementing the user signup which is structured in the following way:
The user posts the signup form
The request is routed to a controller action which maps all parameters like e-mail, password etc. on a DTO. The different fields of the DTO are annotated with JSR 303 constraints.
The e-mail field is annotated with a constraint validator that makes sure that the same address is not added twice. This validator has an autowired reference to the UserRepository, so that it can invoke it's isExistingEmail method.
The signup method of the user service is called, which basically looks as follows:
#Transactional(isolation = Isolation.SERIALIZABLE)
public User signupUser(UserDto userDto) {
validator.validate(userDto);
userRepository.add(userDto);
return tutor;
}
In case of a validation error the validator.validate(userDto) call inside of the service will throw a RuntimeException.
Please note that the repository's add method is annotated with #Transactional(propagation = Propagation.MANDATORY) while the isExistingEmail method does not have any annotations.
My problem is that when I post the signup form twice in succession, I receive a unique constraint error from the database since both times the userRepository.isExistingEmail call returns false. However, what I would expect is that the second signup call is not allowed to add the user to the repository, as I set the isolation level of the transaction to serializable.
Is this the expected behavior or might there be a JOOQ/spring transactions configuration issue?
I added a TransactionSynchronizationManager.isActualTransactionActive() call in the service to make sure a transaction is actually active. So this part seems to work.
After some more research and reading the documentation on transaction isolation in the postgres manual I have come to realize that my understanding of spring managed transactions was just lacking.
When setting the isolation level to SERIALIZABLE postgres won't really block any concurrent transactions. Instead it will make use of predicate locks to monitor if a committed transaction would produce a result that is different than actually running concurrent transactions one after another.
An exception will only be thrown by the underlying database driver if the state of the data is not valid when the second transaction tries to commit. I was able to verify this behavior and to force a serialization failure by temporarily removing the unique constraint on my e-mail field.
My final solution was to reduce the isolation level to READ_COMMITTED and to handle the unique constraint violation exception when invoking userRepository.add(userDto), since a SERIALIZABLE isolation level is not really necessary to deal with this particular use case.
Please let me know of any better ways of dealing with this kind of standard situation.

Spring and JUnit, the difference of annotating classes and methods with #Transaction?

I would like to understand that if I annotate my junit class with #Transactional, Spring will create only one transaction which will be shared among my #Test methods and rolled back at the end. Whereas if instead I mark each single #Test with #Transactional, a new transaction will be created and rolled back on a #Test basis. I didn't quite find the expected behaviour in the official documentation (link).
Putting #Transactional on a class level is equivalent to putting it on each of the test methods.
I don't think there is an easy way of achieving your first scenario, ie, a single transaction for all the tests. I am not sure it would make much sense anyway since tests will be executed in a random order so you cannot rely on seeing each others modifications. Of course you can always explicitly call your methods from a single uber-test with a single transaction.
#Transactional at JUnit test case class level will start new transaction before each test method and roll it back afterwards.
You cannot easily start new transaction at the beginning of test class and keep it open for the whole class, at least Spring is not supporting this. Your first workaround would be to use #BeforeClass/#AfterClass pair, but they must be static thus you don't have access to transactional manager.
But first ask yourself a question, why do you want to do this? Sounds like one test depends on the output or database side effects of the other. This is a big anti-pattern in testing, not to mention JUnit does not guarantee the order in which test methods are executed. Maybe you just need a database setup before each test case? #Before and #After are executed within the context of Spring transaction, so you can use them there.

spring scope for transaction

I'm a new Spring user.
I have a question about scope and transaction.
For example, there's a service:
<bean id="bankInDaoService" class="service.dao.impl.UserDaoServiceImpl">
Let's say there are 2 people who want to bank-in at the same time.
And I already put #Transactional above for Hibernate transaction the method for bank-in purpose.
My questions are:
Since default Spring scope is singleton. Will these 2 people share the same values. (person 1 bank-in 500, person 2 bank-in 500)?
Will the #Transactional be effective? I mean let the first person finishes bank-in, and then person 2.
I'll be really thankful for your help.
You have wrongly understood the useage of #Transactional annotation.
#Transactional annotation is used in case where you want to get all or none of your transactions to be successful. If any of the transaction fails then other successful transaction will be rolled back. It is not for synchronisation.
If you have registration page where you take input for 10 fields and 5 are for table user and 5 are for table company and you are inseting both records from a single service function. At that time you should use #Transactional annotation. If insertion is successful in user table and fails in company table then the user table record will be rolled back.
Hope this helps you. Cheers.
You are correct that by default Spring beans are singletons. But this won't be a problem unless your implementation modifies some internal state on each invocation (which would be rather odd - typically a service method will just work with the parameters it's been given).
As I just alluded to, each service method invocation will have its own parameters; i.e.:
deposit(person1_ID, 500)
deposit(person2_ID, 750)
As you've said "at the same time" we can safely assume we have a multi-threaded server that is handling both these people simultaneously, one per thread. Method parameters are placed on the stack for any given thread - so as far as your service is concerned, there is absolutely no connection/chance of corruption between the two people's deposits.
Now turning to the #Transactional annotation: Spring uses "aspects" to implement this behaviour, and again these will be applied separately to each thread of execution, and are independent.
If you're looking for #Transactional to enforce some kind of ordering (for example, you want person2 to withdraw the exact amount person1 deposited) then you need to write a new method that performs both operations in sequence within the one #Transactional scope.

Resources