I'm trying to implement transactions on a CrudRepository Interface. I'm a beginner with this and my current problem is that when receiving a lot of requests from different clients, I'm sometimes getting a duplicate.
To avoid that I wanted to use SQL Transactions and their implementation with Spring but I'm unable to get it working.
Here is how I've tried to do it :
#Repository
#EnableTransactionManagement
#Transactional
public interface ApplicationPackageDao extends CrudRepository<ApplicationPackage, Long> {
/**
* Find if a record exists for this package name ,
* #param packageName
* #return
*/
#Transactional
ApplicationPackage findByPackageName(String packageName);
}
However it doesn't seem to work.
I tried to add the #Transactionnal annotations earlier in the Java methods I'm calling but I can't get it working either.
How am I supposed to work with transactions on CrudRepository ?
Or am I using completely the wrong thing?
In addition to crm86's answer some more notes to the #Transactional annotation:
It seems to be best practice to annotate the entry points into your application (e.g. your web controller methods or the main method of a scheduled batch). By using the annotation attribute TxType you can ensure constraints/conditions in methods which are located deeper in your application (e.g. TxType.MANDATORY would throw if no trx-context is running, etc.).
The #Transactional annotation has only an effect if the class is loaded as spring bean (e.g. #Component annotation at class level).
Remember that only RuntimeException's lead to a rollback. If you want a checked Exception leading to a rollback you have to enumerate each such Exception by using the attribute rollbackOn.
The annotation at class level is valid for all public methods of this class. Method level annotations override those at the class level. The repeated annotation in your example above (first at class level, then at method level) has no effect.
What I suggest:
Check your context and configuration classes with #Configuration annotation. From the documentation:
The #EnableTransactionManagement annotation provides equivalent
support if you are using Java based configuration. Simply add the
annotation to a #Configuration class
#EnableTransactionManagement and only looks
for #Transactional on beans in the same application context they are
defined in
Then you could use #Transactional in your service even in a method
Hope it helps
Related
I have a Spring boot application which has a UserRepository but I have not annotated that repository with #Repository but still my application works fine.
Why #Repository annotation is optional in Spring boot and what is the best practise that should we annotate it or not in repository classes.
It is working because spring will scan the class path and identify given class is repository (DAO) based on the imports, when use #Repositary spring context know how to handle expections like re-throw to pre defined methods ..etc, and also this annotation help readability of the code.
For #Repository annotation even if you haven't mentioned Spring recognizes the repositories by the fact that they are extending Repository interfaces like JPARepository or CrudRepository.
So, it's not mandatory to mention annotation, you can check in your code whether you have mentioned #EnableJpaRepositories("packages") above Main class or not. That might also be one of the reasons why it is working.
As per best practices for annotations, they have a purpose to fulfill, #Repository is important from a database connection perspective, where it has lots of proper exceptions throw, or pre-defined methods.
If you do not use the proper annotations, you may face commit exceptions overridden by rollback transactions. You will see exceptions during the stress load test that is related to roll-back JDBC transactions.
For more clarity have a look at this post
I assume you have somewhere implicitly or explicitly a EnableJpaRepositories annotation which scans all Spring repositories and your repositories are extending CrudRepository or other Spring Data base classes.
The annotation is only needed when defining you own repositories without using Spring Data mechanism. Note, that Repository is just a special Component, latter should also do it to have DI enabled, so this is more for better recognizing the purpose of the class.
#Repository is used to indicate interface that extends a jpa repository, for example. Notice in the example below, the interface extends the Beer class and the BeerQueries query interface.
package com.algaworks.brewer.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.algaworks.brewer.model.Beer;
import com.algaworks.brewer.repository.helper.beer.BeersQueries;
#Repository
public interface Beers extends JpaRepository<Beer, Long>, BeersQueries {
}
I got interested in how Spring's #Transactional works internally, but everywhere I read about it there's a concept of proxy. Proxies are supposed to be autowired in place of real bean and "decorate" base method with additional transaction handling methods.
The theory is quite clear to me and makes perfect sense so I tried to check how it works in action.
I created a Spring Boot application with a basic controller and service layers and marked one method with #Transactional annotation. Service looks like this:
public class TestService implements ITestService {
#PersistenceContext
EntityManager entityManager;
#Transactional
public void doSomething() {
System.out.println("Service...");
entityManager.persist(new TestEntity("XYZ"));
}}
Controller calls the service:
public class TestController {
#Autowired
ITestService testService;
#PostMapping("/doSomething")
public ResponseEntity addHero() {
testService.doSomething();
System.out.println(Proxy.isProxyClass(testService.getClass()));
System.out.println(testService);
return new ResponseEntity(HttpStatus.OK);
}}
The whole thing works, new entity is persisted to the DB but the whole point of my concern is the output:
Service...
false
com.example.demo.TestService#7fb48179
It seems that the service class was injected explicitly instead of proxy class. Not only "isProxy" returns false, but also the class output ("com.example.demo.TestService#7fb48179") suggests its not a proxy.
Could you please help me out with that? Why wasn't the proxy injected, and how does it even work without proxy? Is there any way I can "force" it to be proxied, and if so - why the proxy is not injected by default by Spring ?
There's not much to be added, this is a really simple app. Application properties are nothing fancy either :
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=superSecretPassword
spring.datasource.url=jdbc:mysql://localhost:3306/heroes?serverTimezone=UTC
spring.jpa.hibernate.ddl-auto=create-drop
Thank you in advance!
Your understanding is correct, but your test is flawed:
When the spring docs say "proxy", they are referring to the pattern, not a particular implementation. Spring supports various strategies for creating proxy objects. One of these is the java.lang.reflect.Proxy you tested for, but by default spring uses a more advanced technique that generates a new class definition at runtime that subclasses the actual implementation class of the service (and overrides all methods to apply transaction advice). You can see this in action by checking testService.getClass(), which will refer to that generated class, or by halting execution in a debugger, and inspecting the fields of targetService.
The reason that toString() refers to the original object is that the proxy implements toString() by delegating to its target object, which uses its class name to build the String.
Context
I am using AOP to create/remove Spring ACL records for managed entities by intercepting Spring Data repositories' save and delete methods. All my repositories are #RepositoryRestResource annotated interfaces extending either PagingAndSortingRepository or CrudRepository. This has been working perfectly in the past. Unfortunately I can't identify exactly the point in time (or code change) where it stopped working.
Expected behaviour
The following advice should fire on all save methods.
#Pointcut("execution(* org.springframework.data.repository.*.save(*))")
public void defaultRepoSave() {}
// may be useful for overridden methods
#Pointcut("execution(* com.xxx.yyy.repository.*.save(..))")
public static void customRepoSave() {}
#Pointcut("defaultRepoSave() || customRepoSave()")
public static void repoSave() {}
#Around(value="repoSave() && args(entity)", argNames="entity")
public Object aroundSave(ProceedingJoinPoint pjp, Object entity) throws Throwable{
log.info("Saving ...");
return pjp.proceed();
}
Note: I tried various combinations of #EnableAspectJAutoProxy(exposeProxy=true/false, proxyTargetClass=true/false), but it doesn't seem to have any effect on this particular advice
The issue
The advice fires for some repositories, and not for others. Both repositories are in the same package. Debugging shows that both repositories are proxied, but the execution for the one on the left is missing advice-related interceptors altogether. The one on the right proceeds as expected.
To eliminate the possibility of pointcut mismatch I created a custom annotation and added to the .save() method in both repositories.
Annotation:
#Retention(RUNTIME)
#Target(METHOD)
public #interface AclManaged {}
Used as:
#Override
#AclManaged
Entity1 save(Entity1 entity);
And the advice:
#Around("#annotation(aclManaged)")
public Object aclManaged(ProceedingJoinPoint joinPoint, AclManaged aclManaged) throws Throwable {
log.info("Acl managed");
return joinPoint.proceed();
}
Same story - the annotation works in one repository but doesn't fire for the repository where the execution(..save..) pointcut had failed.
For testing purposes I am invoking both methods by POSTing an empty entity from Postman to each respective repository rest endpoint. However the same issue happens when the repository is invoked directly from my code.
The repositories code for the sake of completeness (the inconsistent behaviour occurs even with the most basic repo implementation):
--- EDIT: I simplified the repositories to the bare minimum ---
#RepositoryRestResource(collectionResourceRel = "entity1s", path = "entity1s")
public interface Entity1Repository extends PagingAndSortingRepository<Entity1, Long> {
// this is the method that is supposed to fire two distinct advices
#Override
#AclManaged
Entity1 save(Entity1 entity);
}
#RepositoryRestResource(collectionResourceRel = "entity2s", path = "entity2s")
public interface Entity2Repository extends PagingAndSortingRepository<Entity2, Long> {
// both aspects match perfectly
#Override
#AclManaged
Entity2 save(Entity2 ics);
}
Questions
What can be blocking the AOP aspects? How does Spring populate the invocation interceptor chain?
What's the best way to troubleshoot AOP aspects? (given both execution and annotation pointcuts fail)
On a slightly different note - is it advisable to use JPA auditing for ACL management instead of AOP?
Versions
spring 5.0.8.RELEASE, spring data rest 3.0.9.RELEASE, spring data jpa 2.0.9.RELEASE (all managed by Spring Boot 2.0.4)
The issue seems to be caused by an unfortunate combination of Spring Booot's #Enable... annotations and my #Configuration classes. The framework seems to have a few different ways to determine class/interface proxying (stemming from #EnableCaching, #EnableTransactionManagement, #EnableAspectJAutoProxy, #EnableAsync). In certain situations they seem to hijack the expected behaviour. I was able to restore the expected behaviour by:
Adding proxyTargetClass=true to all #Enable.. annotations
Moving the #Enable... annotations to relevant #Configuration classes
I wasn't able to determine which combination in particular was causing the inconsistent behaviour i.e. I don't have a minimal replicable test case.
I am still interested in a more erudite insight as to the root causes behind the inconsistent MethodInvocationInterceptor chains.
This question already has answers here:
Spring3 's #Transactional #Scheduled not committed to DB?
(3 answers)
Closed 2 years ago.
I have a question:
Why when we annotate method with #Scheduled and #Transaction, transaction doesn't work?
I know that the #Scheduled call my class instead of proxy class that created by Spring, but can't understand this behavior.
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
#Service
public class UserServiceImpl implements UserService {
#Override
#Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
#Transactional
public void doSomething() {
}
}
I have two solutions of this problem:
Call proxy from Scheduled method.
Implement ConcurrentTaskScheduler
and replace object of ScheduledMethodRunnable(that is with my class)
with object of ScheduledMethodRunnable with proxy.
But this solutions is very inconvenient.
Can you explaim me why #Scheduled works like this?
Thank you!
It happens because to process both annotations MAGIC is used.
I suppose there are several things happens:
UserServiceImpl is created.
#Scheduled annotation is processed and reference to bean is stored to invoke it at appropriate time.
#Transactional annotation is processed. It create proxy which store reference to original bean. Original bean is replaced to proxy in application context.
If step 2 and 3 passed in different order then you had no problem.
I don't know how to control order in which annotation is processed. I don't even sure it is possible at all.
There is basically two solution.
Use different kind of magic to process #Transaction. Default way is to create proxy object, but it is possible to instruct Spring to instrument current class.
Split this to two class each of them will have method with only one annotation.
Example:
#Service
public class UserServiceImpl implements UserService {
#Override
#Transactional
public void doSomething() {
}
}
#Service
public class UserServiceScheduler {
#Inject
private UserService service;
#Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
public void doSomething() {
service.doSomething();
}
}
I'm personally recommend second approach.
The Question is not private or public, the question is: How is it invoked and which AOP implementation you use!
If you use (default) Spring Proxy AOP, then all AOP functionality provided by Spring (like #Transational) will only be taken into account if the call goes through the proxy. -- This is normally the case if the annotated method is invoked from another bean.
This has two implications:
Because private methods must not be invoked from another bean (the exception is reflection), their #Transactional Annotation is not taken into account.
If the method is public, but it is invoked from the same bean, it will not be taken into account either (this statement is only correct if (default) Spring Proxy AOP is used).
you can also use the aspectJ mode, instead of the Spring Proxies, that will overcome the problem. And the AspectJ Transactional Aspects are woven even into private methods (checked for Spring 3.0).
refer: http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/aop.html#aop-proxying
I created a custom sterotype #Action, and Spring has managed to detect it in the package scan I configured in the configurations.
The next step I would like to do is to tell Spring that all classes with #Action should be created with prototype, instead of Singleton.
My #Action interface is as follows:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Action {
}
I tried to mark it with #Scope("prototype") but that does not seem to help.
Is what I desire possible?
Kent
The context:component-scan can be configured with a custom scope-resolver, which implements org.springframework.context.annotation.ScopeMetadataResolver.
I created a custom scope-resolver that not only checks the bean for a #Scope annotation (with the default resolver of org.springframework.context.annotation.AnnotationScopeMetadataResolver), but looks up annotations of annotations too (recursively).
One thing to note though, that looking up annotations recursively might go into an endless loop, as java.lang.annotation.Documented is annotated with java.lang.annotation.Documented. Best to maintain a table that indicates which annotation has been looked up already.
Unfortunately not with spring 2.5.X. Your #Component-annotation describes the role of the bean while the scope is a separate axis, so a role and scope descriptor typically have to be applied separately to the implementation class. When viewed in this way it makes some sense (edit: at least it did so for a few seconds, anyway)
I don't know how this will change i spring 3.x, which is not too far away. There seems to be some room for improvement.