Unable to enforce Propagation.MANDATORY on Spring Data CRUD methods - java

Any reason for this?
I've been using Spring data for years but I don't think I've ever unit tested one of their out-of-the-box CRUD methods before.
Why is it that the following interface definition has no effect on their transactional implementation for CRUD methods?
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
Stream<MyEntity> findMyEntityByStatusEquals(Status status);
}
If I call myRepository.save(new MyEntity()) from a test method, WITHOUT my test being wrapped in a transaction, it succeeds.
However, if I call myRepository.findMyEntityByStatusEquals("MY_STATUS") it fails stating that it needs to be wrapped in a transaction.
The latter case I expect, the former case terrifies me as I don't seem to be able to enforce it to be part of an existing transaction.
::Edit:: Turns out putting #Transactional at the top of the interface has no effect on Spring Data CRUD methods that have previously been marked as #Transactional. I always assumed it was also an override when specified on these interfaces.

As stated by documentation here
CRUD methods on repository instances are transactional by default. For
reading operations the transaction configuration readOnly flag is set
to true, all others are configured with a plain #Transactional so that
default transaction configuration applies.
#Transactional has Propagation.REQUIRED as its default propagation type, so when you call the save method a new transaction just begin.
If you want force Propagation.MANDATORY even on built-in CRUD methods you have to override such methods, i.e
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
Stream<MyEntity> findMyEntityByStatusEquals(Status status);
#Transactional(propagation = Propagation.MANDATORY)
public <MyEntity> MyEntity save(MyEntity entity) {
super.save(entity);
}
}
hope this helps

Related

Public method with #Transactional calling private method

I have a few questions about #Transactional annotation. If I have an implementation similar as below:
#Transactional
public Interface ExampleRepo {
SampleClass createRecord();
}
public class RepoImpl implements ExampleRepo {
public SampleClass createRecord() {
return saveRecord();
}
private saveRecord() {
//Saving record to Database.
}
}
Will the transaction rollback as Spring will ignore if the #Transactional was called on a private method? But what if a public method annotated with #Transactional calls a private method that actually does the database operation.
Will this transaction be marked as rollback?
As per Spring Documentation
Spring recommends that you only annotate concrete classes (and methods
of concrete classes) with the #Transactional annotation, as opposed to
annotating interfaces. You certainly can place the #Transactional
annotation on an interface (or an interface method), but this works
only as you would expect it to if you are using interface-based
proxies. The fact that Java annotations are not inherited from
interfaces means that if you are using class-based proxies (
proxy-target-class="true") or the weaving-based aspect (
mode="aspectj"), then the transaction settings are not recognized by
the proxying and weaving infrastructure, and the object will not be
wrapped in a transactional proxy, which would be decidedly bad.
So, I would recommend to use #Transaction at method createRecord() level or RepoImpl class level.
All code within a transaction scope runs in that transaction. However, you can specify the behavior if a transactional method is run when a transaction context already exists.
When a method without #Transactional is called within a transaction block, the parent transaction will continue to exist for the new method. It will use the same connection from the parent method (method with #Transactional) and any exception caused in the called method (method without #Transactional) will cause the transaction to rollback as configured in the transaction definition.
Since this mechanism is based on proxies, only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with #Transactional.

Are autowired repositories thread safe in spring boot?

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

How to join parent transation in spring using #Transactional annotaion

I am very new to transactions in spring.Due to some code standards used by my organization , i asked to join parent transaction if exist while calling any method.
My application is a Spring MVC application having three layers
Web Layer(controller classes)
Service Layer(Service class containing business logic)
DAO Layer (DAO(Data access layer) classes for DB related query )
Now in a method on service layer uses three different methods of dao layer. I have annotated this service method as transactional in nature by using #Transactional. Now i want all three dao methods called from this service layer methods also be transactional in nature and must join the parent transaction started by service layer method insisted of starting other new translations for each dao methods.
You need to annotate your service method with a REQUIRES_NEW propagation. This would mark the start of transaction. By default, the dao methods if called by this method would inherit the transactional behavior and use the existing transaction.
However, if you want to represent the transactional boundaries in your code, you can annotate them with a REQUIRED (participate in transaction if existing or create a new if doesn't exist) or MANDATORY (participate in transaction if existing, otherwise throw an exception).
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void serviceMethod() {}
#Transactional(propagation = Propagation.REQUIRED)
public void daoMethod1() {}
#Transactional(propagation = Propagation.REQUIRED)
public void daoMethod2() {}
You can use the propagation element in the #Transactional annotation with the property Propagation.MANDATORY. With this, the method supports the current transaction or throws an exception if there is no active transaction. So, in your DAO layer, you can do something like this :
#Transactional(propagation=Propagation.MANDATORY)
public void daoMethod() { // some logic }

Spring transaction propagation - Service vs DAO

I have a Service class like below:
#Service("MyService")
public class MyService {
#Autowired
MyDao dao;
public void process() {
getFromDao();
// getMoreFromDao();
// process();
// if all good, then
doStuff();
}
public void getFromDao() {
// do some stuff
dao.getData();
}
#Transactional(transactionManager="simpleDatasourceTxMgr", propagation=Propagation.REQUIRED)
public void doStuff() {
dao.saveData(1);
dao.saveData(2);
dao.saveData(3);
}
}
The DAO called is:
#Repository
public class MyDao {
#Autowired
#Qualifier("myjdbcTemplate")
NamedParameterJdbcTemplate jdbcTemplate;
public void saveData(obj a) {
jdbcTemplate.execute("Query", ...);
}
}
I want my doStuff() method in the service class to run within a transaction and rollback everything if there is an exception in the saveData() method. But this is not running in transaction.
If I add #Transaction to a DAO method looks like it runs in separate transaction. Is this correct?
Update: I have added a process() method to my Service and I call getFromDao() and doStuff() from process(). process() is called from the controller. So looks like if I make the service class #Transactional, then everything executes within a transaction. But I don't want getFromDao() to execute in transaction.
We use just JDBC and no Hibernate.
You can place the #Transactional annotation before an interface
definition, a method on an interface, a class definition, or a public
method on a class. However, the mere presence of the #Transactional
annotation is not enough to activate the transactional behavior. The
#Transactional annotation is simply metadata that can be consumed by
some runtime infrastructure that is #Transactional-aware and that can
use the metadata to configure the appropriate beans with transactional
behavior. In the preceding example, the
element switches on the transactional behavior.
Or if you want annotations you can enable it with
It is not sufficient to tell you simply to annotate your classes with
the #Transactional annotation, add #EnableTransactionManagement to
your configuration, and then expect you to understand how it all
works. This section explains the inner workings of the Spring
Framework’s declarative transaction infrastructure in the event of
transaction-related issues.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

Strange behaviour for chainned transactional annotation

I don't get how it works the transactional annotations of Spring. So I made the next test with no practical sense but I It shows my problem:
public class TransactionalTest {
public void noTransaction(){
required();
}
#Transactional(propagation = Propagation.SUPPORTS)
public void supports(){
required();
}
#Transactional(propagation = Propagation.REQUIRED)
public void transaction(){
required();
}
#Transactional(propagation = Propagation.REQUIRED)
public void required(){
mandatory();
}
#Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.MANDATORY)
public void mandatory(){
doSomething();
}
private void doSomething(){
//I don't feel like to do something.
}
}
Methods noTransaction, supports and transaction call to the same method: required but only the last one (transaction) works properly. The two others give me back the message No existing transaction found for transaction marked with propagation 'mandatory'.
In the real case, I have some non transactional methods which calls to transactional methods annotated with REQUIRED (It works fine). but if a non transactional method calls to a transactional method (annotated with REQUIRED) which it calls to another transactional method annoted with MANDATORY, it fails.
Why this behaviour and how can avoid it? I annotated annotate all the method which calls to a transaccional method just in case?
If you're using the default AOP method, Spring Proxy AOP, the advice is implemented by wrapping the annotated class with a proxy object that intercepts and advises the calls. When you use self-calls like this, you're bypassing the proxy, and the advice doesn't get applied.
If you really do need to have advice applied on self-calls, you need to use AspectJ AOP, which actually modifies the class in question instead of decorating it.

Categories