I have a method something like:
#Transactional
public void method1() {
taskExecutor.execute() -> {
// do work here
}
}
The #Transactional annotation isn't honored due to new thread pool.
How can I fix this?
You can try to write a Class with a method of your work(will do) and set the #Transactional annotation on this method.Then inject this Class and invoke its work method in new thread pool.
For example:
public class WorkClass {
#Transactional
public void work() {
//do work here...
}
}
#autowired
private WorkClass workClass;
#Transactional
public void method1() {
taskExecutor.execute() -> {
workClass.work();
}
}
And you can adjust policy by Spring Transaction Propagation.
Related
I need to write integration test for "processEvent" method which calls a #Async method inside it. I tried writing a test for this method, but the issue was it did not save the Student object to the DB. However removing #Async annotation allows me to save the object. I want to know how should I write Test cases for this method, eliminating the #Async issue. I want to save the student object while testing. I have attached my code and below.
Here is the ClassA and it has the method I want to test.
#Service
public class ClassA {
private final ConfigurationProcessor<Student> configurationProcessor;
#Autowired
public ClassA(ConfigurationProcessor<Student> configurationProcessor) {
this.configurationProcessor = configurationProcessor;
}
public void processEvent(Configuration configuration) {
configurationProcessor.process(configuration);
}
}
This is the interface ConfigurationProcessor class
public interface ConfigurationProcessor<T> {
void process(Configuration configuration);
}
And this is its Impl class
#Service
public class ConfigurationProcessoeStudents10Impl implements ConfigurationProcessor<Student> {
private final StudentRepository studentRepository;
#Autowired
public ConfigurationProcessoeStudents10Impl(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
#Override
#Async
public void process(Configuration configuration) {
studentRepository.save(Student.builder().Name(configuration.name).Age(configuration.age));
}
}
This is the Test I have written so far.
#EnableAutoConfiguration
#SpringBootTest
public class AudienceC10IT {
#Autowired
ClassA classA;
#Test
#Tag("VerifyProcess")
#DisplayName("Verify kafka event consumer from configuration manager")
void verifyProcess(){
Configuration configuration = new Configuration("lal",12);
classA.processEvent(configuration);
}
}
If you have have set up a ThreadPoolTaskExecutor bean you can Autowire it in your test.
Then, after you call your async method, you can await the termination.
Then you can check for the expected behaviour / result.
Something like this:
#Autowired
private ThreadPoolTaskExecutor asyncTaskExecutor;
#Test
void test() {
callAsyncMethod();
boolean terminated = asyncTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
assertAsyncBehaviour();
}
I have an issue with rollbacking in #Transactional. When rollback is called, rollback itself is working, but another method after is executed.
#Transactional(rollbackFor = { Exception.class })
#Service
public class Service {
public void method1() {
stuffToGetListOfObjects();
deleteAllAndSaveAll(listOfObejcts);
Util.staticMethod();
}
private deleteAllAndSaveAll(List list) {
repository.deleteAll();
repository.saveAll(list);
}
}
#SpringBootApplication
public class Application implements ApplicationRunner {
private final Service service;
#Autowired
public Application(Service service) {
this.service = service;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
service.method1();
}
}
When something goes wrong during the insertion in repository.saveAll(list), no data is actually deleted which is fine and expected. The issue is program is going on the Util.staticMethod() method is executed "after" rollback.
I know that static methods cannot be #Transactional and private methods are ignored, but that doesn't seem the issue here. According to the log, everything in method1() is executed first and after that, the inserting is happening. I guess I need to pick out the Util.staticMethod() calling from transaction somehow.
That's why you should separate the repository actions from the service functions. Repository actions are rollbacked. If you separate them the repository level will throw an error and rollback itself. And before the static method, you can check whether the transaction is finished or not by #transactionaleventlistener.
I'm using #KafkaListener and I need a dynamic topic name so I use the SpEL '__listener' in order to do that
#PostConstruct
public void init() {
myProps= generateTopicDynamically();
}
#KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(#Payload MyObject myObject) {
//Do something with my event
}
It works perfectly well.
The main issue is when I want to add another annotation that trigger some Aspect programmation
#MyCustomAnnotationToRecordPerformance
#KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(#Payload MyObject myObject)
and here the aspect class
#Aspect
#Configuration
#Slf4j
public class MyCustomAnnotationToRecordPerformanceAspect {
#Pointcut("#annotation(MyCustomAnnotationToRecordPerformance)")
public void annotationMyCustomAnnotationToRecordPerformance() {
}
#Around("annotationMyCustomAnnotationToRecordPerformance()")
public Object doSomething(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
return proceedingJoinPoint.proceed();
}
}
I have this issue because Spring try to resolve __listener before #PostConstruct has been called.
Caused by: java.lang.IllegalArgumentException: #KafKaListener can't resolve 'null' as a String
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.resolveAsString(KafkaListenerAnnotationBeanPostProcessor.java:648)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.resolveTopics(KafkaListenerAnnotationBeanPostProcessor.java:520)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.processListener(KafkaListenerAnnotationBeanPostProcessor.java:419)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.processKafkaListener(KafkaListenerAnnotationBeanPostProcessor.java:370)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.postProcessAfterInitialization(KafkaListenerAnnotationBeanPostProcessor.java:298)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
... 41 common frames omitted
I tried to debug it
We can see lot of CGLIB reference, so bean has been already proxified, but all properties are null. So I supposed Autowired and PostConstruct method has not been called yet
For now, I tried to delay the processor that manage #KafkaListener, but I was not able to find where I can change that without have to redefine fully Kafka configuration
#EnableKafka import KafkaListenerConfigurationSelector that is DeferredImportSelector.
Here the comment on this class
A {#link DeferredImportSelector} implementation with the lowest order to import a {#link KafkaBootstrapConfiguration} as late as possible.
So I supposed it already delay as late as possible based on the comment
I test it with #Transactional, and I have the same issue.
#Transactional
#KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(#Payload MyObject myObject)
Do have any idea about it?
The only alternative I see for now is split my class in 2 and create 2 beans.
KafkaListener method call the other bean. But I found very strange to have to do that.
Thanks in advance for you help.
I just tested it with #Transactional and it works as expected for me - I have confirmed that we already have a CGLIB proxy by the time we get to the #KafkaListener annotation BPP...
#SpringBootApplication
#EnableTransactionManagement
public class So69817946Application {
public static void main(String[] args) {
SpringApplication.run(So69817946Application.class, args);
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so69817946").partitions(1).replicas(1).build();
}
}
#Component
class listener {
public String getTopic() {
return "so69817946";
}
#Transactional
#KafkaListener(id = "so69817946", topics = "#{__listener.topic}")
public void listen(String in) {
System.out.println(in);
}
}
#Component
class TM extends AbstractPlatformTransactionManager {
#Override
protected Object doGetTransaction() throws TransactionException {
return new Object();
}
#Override
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
}
#Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
}
#Override
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
}
}
so69817946: partitions assigned: [so69817946-0]
And I can see the transaction interceptor in the call stack.
So, yes, an MCVE would be helpful.
Thanks to the help of Gary, I found the solution.
Once we have aspect, the class is proxified and properties became null in the CGLIB object.
We need to call getter in order to have the value from original object, not the proxified one
SpEL is able to read public getter that will be executed on the original object, and not the CGLIB one
So the solution was simply to create a public getter for my private
public String getMyProps(){
return this.myProps;
}
Thanks all.
Case 1
Let's consider the following Spring configuration:
#Configuration
public class MyConf1 {
#Bean
public Foo getFoo() {
// Foo class is defined as part of an external lib.
return new Foo();
}
#Bean
public Bar getBar() {
return new Bar(getFoo());
}
}
For some reasons, I need to invoke a Foo's method (i.e. myFoo.shutdown();) when MyConf1 is destroyed.
Is there any way to perform this operation without retrieving the bean instance directly from the application context (via ApplicationContext.getBean())?
Case 2
Again, let's consider a second Spring configuration class:
#Configuration
public class MyConf2 {
#Bean
public ScheduledJob scheduledJob() {
Timer jobTimer = new Timer(true);
return new ScheduledJob(jobTimer);
}
}
This time, I need to invoke jobTimer.cancel() before destroying MyConf2. Indeed, I can instantiate jobTimer outside scheduledJob(), or making it a method's parameter, as scheduledJob(Timer jobTimer).
It will then be possible to define a proper destroyer method for MyConf2. However, I would like to know if there are other ways to proceed.
Any good suggestion?
Note: Foo, Bar, Timer, ScheduledJob classes are defined externally. Thus, there is no possibility to explicitly define an inner destroy method. As assumption, I can modify only MyConf1 and MyConf2.
I would suggest defining a destroy() method (annotated with #PreDestroy) in Foo class
Similarly, modify ScheduledJob class like
public class ScheduledJob {
private Timer timer;
public ScheduledJob(Timer timer){
this.timer = timer;
}
#PreDestroy
public void destroy(){
timer.cancel();
}
}
And add destroyMethod param in #Bean
#Configuration
public class MyConf2 {
#Bean(destroyMethod = "destroy")
public ScheduledJob scheduledJob() {
Timer jobTimer = new Timer(true);
return new ScheduledJob(jobTimer);
}
}
Please see the following page http://forum.spring.io/forum/spring-projects/container/48426-postconstruct-and-predestroy-in-javaconfig
DisposableBean should help you with case #1.
You can implement the DestructionAwareBeanPostProcessor interface that can adds a before-destruction callback when the bean is destroy.In that interface,the method postProcessBeforeDestruction is do that,see the following:
#Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
System.out.println("before destory:"+bean);
}
#Override
public boolean requiresDestruction(Object bean) {
return true;
}
Pay attention to that the method requiresDestruction should return true,otherwise the method postProcessBeforeDestruction will not call when bean should destroy.
And i have a test:
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:application-main.xml");
applicationContext.registerShutdownHook();
}
The postProcessBeforeDestruction really call when the bean is destroy.The output is :
before destory:com.zhuyiren.spring.learn.util.Handler#55141def
before destory:com.zhuyiren.spring.learn.controller.TestControleler#47eaca72
before destory:com.zhuyiren.spring.learn.service.impl.TestServiceImpl#7b2bbc3
before destory:com.zhuyiren.spring.learn.service.impl.TwoServiceImpl#48f2bd5b
before destory:com.zhuyiren.spring.learn.controller.ConverConroller#72967906
before destory:org.springframework.context.event.DefaultEventListenerFactory#1a482e36
before destory:org.springframework.context.event.EventListenerMethodProcessor#77fbd92c
I'm using a UnitOfWork in a background task method (operated by Quartz) with Guice-persist on top of hibernate. The background task call a service, which need to commit the current transaction in the middle of it's task - and continue on another transaction. How can I commit the current UnitOfWork and create a new one?
class BackgroundJob {
#Inject UnitOfWork unitOfWork;
#Inject MyService myService;
public void run() {
try {
unitOfWork.begin();
myService.foo();
} finally {
unitOfWork.end();
} } }
class MyServiceImpl implements MyService {
#Override public void foo() {
foo1();
// I would like to commit here and start a new transaction
foo2();
} }
The service is also managed by Guice, but is a singleton, and do not have access to the caller UnitOfWork as is.
Ideally I do not want to change service signature. A workaround is for the caller to give two UnitOfWork as parameters to foo(), but this seems a bit hacked.
EDIT: For ease of use of future fellow reader, here is my implementation of the solution given by ColinD, which fits the bill nicely:
class BackgroundJob {
#Inject UnitOfWork unitOfWork;
#Inject MyService myService;
public void run() {
try {
unitOfWork.begin();
myService.foo();
} finally {
unitOfWork.end();
} } }
class MyServiceImpl implements MyService {
#Override public void foo() {
foo1();
foo2();
}
#Transactional private void foo1() { ... }
#Transactional private void foo2() { ... }
}
If I recall correctly, a unit of work in Guice Persist is not a single transaction. Rather, it represents a single unit of work such as a single web request or, in your case, a single background job. I believe that a single database connection is used for a whole unit of work, but that unit of work may have multiple distinct transactions. In fact, I think that just starting and ending a unit of work will not cause any transactions to be started or ended.
I think what you want to do is to annotate both foo1() and foo2() (but not foo()) with #Transactional. As long as there's no outer transaction wrapping both calls, they'll each run in a separate transaction.
This may fit the bill.
class BackgroundJob {
#Inject UnitOfWork unitOfWork;
#Inject MyService myService;
public void run() {
try {
unitOfWork.begin();
myService.foo1();
unitOfWork.end();
unitOfWork.begin();
myService.foo2();
unitOfWork.end();
} finally {
unitOfWork.end();
}
}
}