I'm working on spring boot and I need to clarify something regarding to transaction management.
For example, I do have 2 classes that run two separate jobs (the first job is to create profile on database and the second job is to call restful application also profile creation but on different system).
This 2 jobs must be in transactional. Both success needed. It should not create any profile on any data store if one of the job is fails)
Since I'm really new in this Spring. I hope to get suggestion and need to know what is the best practice for this scenario.
There is a facade pattern. I suggest to make a facade service to join the logic of two services. Services must be separate, because working with profiles and communicating with other system are different parts of business logic.
For example, there are ProfileService and OuterService to work with profiles and to outer communication. You can write SomeFacadeService to join two methods and wrap it in one transaction. Default propagation of #Transactional is REQUIRED. So transaction will be created on method SomeFacadeService.doComplexJob and methods profileService.createProfile and outerService.doOuterJob will join the current transaction. If exception occurs in one of them whole SomeFacadeService.doComplexJob will be rolled back.
#Controller
public class SomeController {
#Autowired
SomeFacadeService someFacadeService ;
#RequestMapping("/someMapping")
public void doSomeJob() {
someFacadeService.doComplexJob();
}
}
#Service
public class SomeFacadeService {
#Autowired
ProfileService profileService;
#Autowired
OuterService outerService;
#Transactional
public void doComplexJob() {
profileService.createProfile();
outerService.doOuterJob();
}
}
#Service
public class ProfileService {
#Transactional
public void createProfile() {
// create profile logic
}
}
Related
I have a Spring service that does something like that :
#Service
public class MyService {
#Transactional(propagation = Propagation.NEVER)
public void doStuff(UUID id) {
// call an external service, via http for example, can be long
// update the database, with a transactionTemplate for example
}
}
The Propagation.NEVER indicates we must not have an active transaction when the method is called because we don't want to block a connection to the database while waiting for an answer from the external service.
Now, how could I properly test this and then rollback the database ? #Transactional on the test won't work, there will be an exception because of Propagation.NEVER.
#SpringBootTest
#Transactional
public class MyServiceTest {
#Autowired
private MyService myService;
public void testDoStuff() {
putMyTestDataInDb();
myService.doStuff(); // <- fails no transaction should be active
assertThat(myData).isTheWayIExpectedItToBe();
}
}
I can remove the #Transactional but then my database is not in a consistent state for the next test.
For now my solution is to truncate all tables of my database after each test in a #AfterEach junit callback, but this is a bit clunky and gets quite slow when the database has more than a few tables.
Here comes my question : how could I rollback the changes done to my database without truncating/using #Transactional ?
The database I'm testing against is mariadb with testcontainers, so a solution that would work only with mariadb/mysql would be enough for me. But something more general would be great !
(another exemple where I would like to be able to not use #Transactional on the test : sometimes I want to test that transaction boundaries are correctly put in the code, and not hit some lazy loading exceptions at runtime because I forgot a #Transactional somewhere in the production code).
Some other precisions, if that helps :
I use JPA with Hibernate
The database is create with liquibase when the application context starts
Others ideas I've played with :
#DirtiesContext : this is a lot slower, creating a new context is a lot more expensive than just truncating all tables in my database
MariaDB SAVEPOINT : dead end, it's just a way to go back to a state of the database INSIDE a transaction. This would be the ideal solution IMO if i could work globally
Trying to fiddle with connections, issuing START TRANSACTION statements natively on the datasource before the test and ROLLBACK after the tests : really dirty, could not make it work
Personal opinion: #Transactional + #SpringBootTest is (in a way) the same anti-pattern as spring.jpa.open-in-view. Yes, it's easy to get things working at first and having the automatic rollback is nice, but it loses you a lot of flexibility and control over your transactions. Anything that requires manual transaction management becomes very hard to test that way.
We recently had a very similar case and in the end we decided to bite the bullet and use #DirtiesContext instead. Yeah, tests take 30 more minutes to run, but as an added benefit the tested services behave the exact same way as in production and the tests are more likely to catch any transaction issues.
But before we did the switch, we considered using the following workaround:
Create an interface and a service similar to the following:
interface TransactionService
{
void runWithoutTransaction(Runnable runnable);
}
#Service
public class RealTransactionService implements TransactionService
{
#Transactional(propagation = Propagation.NEVER)
public void runWithoutTransaction(Runnable runnable)
{
runnable.run();
}
}
In your other service wrap the external http calls with the #runWithoutTransaction-Method, e.g.:
#Service
public class MyService
{
#Autowired
private TransactionService transactionService;
public void doStuff(UUID id)
{
transactionService.runWithoutTransaction(() -> {
// call an external service
})
}
}
That way your production code will peform the Propagation.NEVER check, and for the tests you can replace the TransactionService with a different implemention that doesn't have the #Transactional annotations, e.g.:
#Service
#Primary
public class FakeTransactionService implements TransactionService
{
// No annotation here
public void runWithoutTransaction(Runnable runnable)
{
runnable.run();
}
}
This is not limited to Propagation.NEVER. Other propagation types can be implemented in the same way:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void runWithNewTransaction(Runnable runnable)
{
runnable.run();
}
And finally - the Runnable parameter can be replaced with a Function/Consumer/Supplier if the method needs to return and/or accept a value.
This is bit of wild idea, but if you are using mysql database, then maybe switch to dolt for tests?
Dolt is a SQL database that you can fork, clone, branch, merge, push and pull just like a git repository.
You can wrap it as testcontainers container, load necessary data on start and then, on start of each test run dolt reset.
If I have a standard CrudRepository and a Controller like below, is this thread safe? I know that this class is treated as a singleton, but wasn't sure if Spring handles repositories in a special way that allows for no handling on my side.
#Controller
public class TestController {
#Autowired
private TestRepository testRepository;
#RequestMapping(path = "/test")
public void addTest() {
TestObj o = new TestObj();
testRepository.save(o);
}
}
public interface TestRepository extends CrudRepository<TestObj, Integer> {
}
The standard CrudRepository is not threadSafe.But i think that you ask about how the data concurrent access are handling?
first you should to know that there are several phenomes that may occur when using transaction simultaneously and can affect the integrity of data such as: lost update, dirty read, unrepeatable read, last commit wins and phantom read.
For managing these phenomes according to your requirement spring offer the possibility to specify the desired isolation level inside #Transactional annotation.
for more information :
https://www.baeldung.com/spring-transactional-propagation-isolation
https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#jpa.bootstrap-mode
I need to make a #Scheduled method that has a list of schemas and for each schema, deletes rows from 2 tables.
#Scheduled(fixedDelay = 10000)
public void scheduleFixedDelayTask() {
List<String> presentSchemas = getPresentSchemas();
for (String schema : presentSchemas) {
deleteFromCustomerTables(schema);
}
}
I've defined deleteFromCustomerTables as #Transactional(propagation = Propagation.REQUIRES_NEW) and inside it i use the EntityManager to delete rows from 2 tables.
In order to make it work i need to add #Transactional to scheduleFixedDelayTask, otherwise i recive a TransactionRequiredException.
My problem is that i do not want the whole scheduler to be #Transactional, if something goes wrong in one schema i do not want to do a rollback of all schemas.
I've also tried without #Transactional and with :
Session session = entityManager.unwrap(Session.class);
Transaction t = session.beginTransaction();
//exec delete
t.commit();
session.close();
But i still recieve TransactionRequiredException.
Do you have a solution?
You have to be sure that you configured the transaction manager something like this:
#Configuration
#EnableTransactionManagement
public class TransactionConfig {
#Bean
#Primary
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
You have to be sure that method deleteFromCustomerTables is not in the same component, something like this:
#Component
class Component1 {
void scheduleFixedDelayTask(){...}
}
#Component
class Component2 {
void deleteFromCustomerTables(){...}
}
But at a very high level, Spring creates proxies for classes that
declare #Transactional on the class itself or on members. The proxy is
mostly invisible at runtime. It provides a way for Spring to inject
behaviors before, after, or around method calls into the object being
proxied. Transaction management is just one example of the behaviors
that can be hooked in. Security checks are another. And you can
provide your own, too, for things like logging. So when you annotate a
method with #Transactional, Spring dynamically creates a proxy that
implements the same interface(s) as the class you're annotating. And
when clients make calls into your object, the calls are intercepted
and the behaviors injected via the proxy mechanism.
How should I create a controller which is thread safe?
As per the best practice controllers are singleton.
Consider the below code in which im storing user data through a autowired service Object ,which makes my code stateful.
How would i make the below code thread safe.
#RestController
class ApiController {
#Autowired
IDbService< User > iDBService;
#RequestMapping(value = "/api/adduser", method = RequestMethod.POST)
public ResponseEntity<User> createUser(#RequestBody User user){
User savedUser=iDBService.create(user);
return new ResponseEntity<User>(savedUser, HttpStatus.CREATED);
}
Here is my Service implementation.
I have shared variable in my service
public class IDbServiceImpl<T> implements IDBService<T>{
#Autowired
GenericRepository<T, Serializable> genericRepository;
#Override
public T create(T object) {
return genericRepository.save(object);
}
}
Your controller is a singleton by default and your service is singleton by default too.
Therefore in order to make them thread safe you have to make sure that the operations that take place inside the service must be thread safe, in case of changing the state of an object inside the service ie. a list.
In case of using a rdbms then you have a transaction related problem.
If you use spring and Jpa, the transaction manager will take care for your updates provided that you use #Transactional. In case of plain jdbc method then you can either use pure jdbc and do the transaction handling on your own or use spring-jdbc that comes with a transaction manager.
If you want the database rows not to be changed in case of a write in progress then you have to take into consideration row-locking related mechanisms. – gkatzioura Feb 7 at 15:23
In case of JPA using #Transactional will do the work. However depending on your application you might have to consider locking. Check this article on locking with jpa.
Controllers are singletons, therefore they should be implemented in a thread safe manner.
Design your application in a way that controllers are stateless. Add transactional support in your #Repository layer.
Example:
public class GenericRepository<T, Serializable> {
#Transactional
public void save(T object) {
// save user
}
}
You could use Spring declarative transaction management mechanism. The #Transactional annotation itself defines the scope of a single database transaction.
Your controller looks thread safe. As there is no instance variable storing the state. User object will be different for each request and will be resolved by the MVC framework.
Due to certain reasons i have manually performed transaction commit and roll back using Spring PlatformTransactionManager, what i need to do is setup a hook so that a post commit action takes place after transaction has been committed.
By looking at:
void commit(TransactionStatus status) throws TransactionException;
I cant see how i can determine a transaction was successful other than assumming it so if no expception are thrown.
And i could use AOP as one option, but what about programmitcally doing it, maybe using callback method?
You could get exactly what you want by a simpler way, with TransactionSynchronizationManager and TransactionSynchronization
With TransactionSynchronizationManager, you have static methods to get information about current transaction, and you can register a TransactionSynchronization wich allows you to automatically do a post-commit as you call that
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
void afterCommit(){
//do what you want to do after commit
}
})
Be aware that the TransactionSynchronization is on a per-thread basis (which is often not a problem for a basic web request).
Credit to Grooveek's answer and Alex's comment under it - I put this here because the combined suggestions provide a solid and cleaner solution that is hard to find around the net.
Using Spring 4+. if you need a callback on a #Transactional method after it successfully commits just add that in the beginning of the method:
#Service
public class OneService {
#Autowired
OneDao dao;
#Transactional
public void a transactionalMethod() {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
public void afterCommit(){
//do stuff right after commit
System.out.println("commit!!!");
}
});
//do db stuff
dao.save();
}
}
Since Spring 4.2 it has been possible to define listeners for post commit events (or more generally transaction synchronization events e.g. rollbacks) using annotation based configuraton. This is based off event handling in core spring. It is easier to test code using this approach since you avoid a direct dependency on TransactionSynchronizationManager, which will likely not be active in a unit test. You can easily test that your transactional service publishes an event and also that your listener performs the right action when you receive an event.
So without further ado this is how to set it up:
In this example we'll assume you have a Customer entity and a CustomerRepository (and ORM to go with it).
First you need a new event type NewCustomerEvent:
// NewCustomerEvent.java
// Just a regular pojo for the event
public class NewCustomerEvent {
public String email;
// constructor, setters and getters omitted
}
Then you define a listener using #TransactionalEventListener. By default this will execute after a sucessful commit but this can be changed using the phase parameter:
// NewCustomerEventListener.java
#Component
public class NewCustomerEventListener {
#TransactionalEventListener
public void handleNewCustomerEvent(NewCustomerEvent newCustomerEvent) {
// handle new customer event
}
}
Finally you augment your transaction service with an ApplicationEventPublisher on which you call publish once all the transaction statements have been sent.
// CustomerRespositoryService.java
#Service
public class CustomerRepositoryService {
#Inject
private ApplicationEventPublisher applicationEventPublisher;
#Inject
private CustomerRepository customerRepository;
#Transactional
public void createCustomer(String email) {
Customer customer = new Customer(email);
customerRespotory.save(customer);
applicationEventPublisher.publish(new NewCustomerEvent(email));
}
}
See also:
https://dzone.com/articles/simpler-handling-of-asynchronous-transaction-bound
https://dzone.com/articles/transaction-synchronization-and-spring-application
In one of my projects because of certain reasons I also had to use PlatformTransactionManager. So I forced to use org.springframework.transaction.support.TransactionTemplate.
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/transaction/support/TransactionTemplate.html
The main benefit is that if you have implemented PlatformTransactionManager correctly, you don't need to bother with manual commit/rollback. At least source code of TransactionTemplate may help you if you need more specific thing.
It's pretty simply to use:
config.xml
<bean name="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="platformTransactionManager"/>
</bean>
MyServiceImpl.java
#Service
public class MyServiceImpl implements MyService {
#Autowired
private TransactionTemplate transactionTemplate;
public Entity getSomethingWithTx(final long id) {
return transactionTemplate.execute(new TransactionCallback<Entity>() {
#Override
public Entity doInTransaction(TransactionStatus status) {
//TODO implement
}
});
}