Can someone explain me why this will work:
#Transactional
#Test
public void test() {
save();
}
public void save() {
Scenario scenar = new Scenario();
sessionFactory.getCurrentSession().save(scenar);
}
And this won't, because it won't find a transaction:
#Test
public void test() {
save();
}
#Transactional
public void save() {
Scenario scenar = new Scenario();
sessionFactory.getCurrentSession().save(scenar);
}
Thank you!
Spring #Transactional annotation works using Spring AOP. This means, that when a bean that contains a method with that annotation is injected as a dependency for a different bean, it gets wrapped in a proxy. This proxy has the same interface as the bean, but performs additional actions before a method is invoked (wrapping it in a transaction in this case). You can think of it as a sort of a decorator. You can even see the proxy being invoked when you debug your application.
Now, when the method that you annotate with #Transactional is called from the same class, there is no (at least, no easy) way to inject the proxy. There just isn't a way to replace the object referenced by the "this" keyword in Java.
More reading on Spring AOP proxies.
As you have a method annotated as #Test I assume this is part of a Junit Test class.
Spring developpers know that test methods usually do not implement interfaces, and as such cannot support JDK proxying. So they specially support #Transactional annotation on a #Test method. The doc says :
Enabling and disabling transactions
Annotating a test method with #Transactional causes the test to be run within a transaction that will, by default, be automatically rolled back after completion of the test. If a test class is annotated with #Transactional, each test method within that class hierarchy will be run within a transaction. Test methods that are not annotated with #Transactional (at the class or method level) will not be run within a transaction. Furthermore, tests that are annotated with #Transactional but have the propagation type set to NOT_SUPPORTED will not be run within a transaction.
Related
I'd like to write integration test to verify proper function of #EventListener(ApplicationReadyEvent.class) annnotated method, but I need to setup the data to be found by some repository. The #SQL is executed only before #Test method, ie. after the ApplicationReadyEvent was processed., #MockBean followed by Mockito.when in #Before method is the same, it's reached only after #SpringBootTest finished initialization, namely after ApplicationReadyEvent was processed.
I also tried to provide #TestConfiguration with bean defined as:
#Bean
SomeRepository someRepository() {
SomeRepository someRepository = Mockito.mock(SomeRepository.class);
Mockito.when(someRepository.findById("value")).thenReturn(...);
return someRepository;
}
but this is somewhat insufficient, as the #SpringBootTest still finds actual implementation of this repository (even if I add #AutoConfigureMockMvc) and run crashes with this conflict.
How can I prepare the mocked repository or actual data in test db for this scenario?
In my Spring application, I have a class annotated with org.springframework.transaction.annotation.Transactional annotation.
Is it possible to discard transactions for a specific single method of this class without removing #Transactional from class level declarations? If so, please show an example
According to Transactional documentation:
public #interface Transactional
Describes a transaction attribute on an individual method or on a class.
So additionally to the class-level annotation just define the correct attribute at your method to disable the default transaction handling.
example:
#Transactional(propagation=Propagation.NEVER)
public long notInTransaction(AnythingData anythingData) throws Exception {
//JDBC code...
}
Will throw an exception if this method is called inside an active transaction. Whereas Propagation.NOT_SUPPORTED will "suspend" the current transaction and make sure that the method is called non transactional.
I use an #Autowired MockHttpServletRequest in some of my Spring tests. TestNG is used as testing framework. If I only have one test method in the class this works fine. However, if there are multiple test methods, only the first run test uses my MockHttpServletRequest. Let me illustrate with an example:
#WebAppConfiguration
#ContextConfiguration({"classpath:applicationContext.xml"})
public class FooTest extends AbstractTestNGSpringContextTests {
#Autowired
private MockHttpServletRequest servletRequest;
#Test
public void test1() {
assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
}
#Test
public void test2() {
assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
}
}
In this example, test1() passes, but test2() fails! If you run the test methods individually, they both pass. Why does one test fail if they are run together?
I tried to dig in the code, there seems to be some kind of reset of the request attributes after a test method have run, but I didn't find a way to turn it off. My Spring version is 3.2.8.RELEASE.
UPDATE: This has been fixed in Spring Framework 3.2.9, 4.0.4, and 4.1. See SPR-11626 for details.
Well, my friend... you have discovered a bug in the Spring TestContext Framework.
The reason for this behavior is that ServletTestExecutionListener resets the request attributes after each test method, but DependencyInjectionTestExecutionListener does not re-inject dependencies before each test method (by default). When the second test method is executed, the servletRequest field is still referencing the MockHttpServletRequest that was created for the previous test method; whereas, ServletTestExecutionListener creates a new instance of MockHttpServletRequest for each test method and sets it in the request attributes. Thus, the injected request and the one stored in the RequestContextHolder are only the same for the first test method that executes in TestNG.
Since I am the author of this code, I have to personally apologize, but... I'll make sure it gets fixed ASAP. See SPR-11626 for details on the status of the fix. ;)
Note: this bug only applies to TestNG tests; this does not apply to JUnit tests.
As a work-around, you can annotate the affected test methods with #DirtiesContext (or annotate your test class with #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)). This will allow your tests to pass as you expect.
The use of #DirtiesContext will make Spring close your test ApplicationContext after each test method, and this will likely have a negative impact on the speed of your tests; however, as of Spring 3.2.8 and 4.0.3, this is the only non-custom solution.
Having said that, the following is a much more efficient work-around. Just define this custom TestExecutionListener in your project:
public class AlwaysReinjectDependenciesTestExecutionListener extends AbstractTestExecutionListener {
public void afterTestMethod(TestContext testContext) throws Exception {
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
}
And then annotate your test class like this:
#TestExecutionListeners(AlwaysReinjectDependenciesTestExecutionListener.class)
That should clear up any issues and keep your test suite running quickly.
Regards,
Sam
I use spring 3.2 and Hibernate 4 in my project. When i query table i get a "No Session found for current thread" message. I try to use #Transactional annotation(it get success) but i don't want to put #Transactional to every service implementation.
Is there an another way?
In other words "How can i do a simple "insert" operation without using #Transaction?"
Thx...
You should not have #Transactional on you DAO methods, in fact you should never be accessing your DAO methods directly, you should be using an #Service. A service will use zero or more DAO classes to perform operations, only after all operations are completed will the transaction be committed.
#Repository
public class CustomerDao() {
// dao methods here, they are not transactional but will be run within a sevice transaction
}
#Service
#Transactional
public class CustomerService() {
private final CustomerDao customerDao;
#Autowired
public CustomerService(CustomerDao customerDao) {
this.customerDao = customerDao;
}
//service methods here (they are all transactional because we have annotated the class)
}
#Transactional is used for making a java code call in transaction so that in case any exception occurred during the process then all database changes will be rolled back. In ideal scenario every service which you think should be independent should have #Transactional annotation. Hibernate also want each database calls in transaction thats why they have implemented in such a way that Transaction will be required for each database query to be successful. I am not sure why you wanted your service to be out of transaction still they would like to fire database calls.
The spring framework documentation states:
In the unlikely case that a test may
'dirty' the application context,
requiring reloading - for example, by
changing a bean definition or the
state of an application object -
Spring's testing support provides
mechanisms to cause the test fixture
to reload the configurations and
rebuild the application context before
executing the next test.
Can someone elaborate this? I am just not getting it. Examples would be nice.
Each JUnit test method is assumed to be isolated, that is does not have any side effects that could cause another test method to behave differently. This can be achieved by modifying the state of beans that are managed by spring.
For example, say you have a bean managed by spring of class MySpringBean which has a string property with a value of "string". The following test method testBeanString will have a different result depending if it is called before or after the method testModify.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/base-context.xml"})
public class SpringTests {
#Autowired
private MySpringBean bean;
#Test public void testModify() {
// dirties the state of a managed bean
bean.setString("newSring");
}
#Test public void testBeanString() {
assertEquals("string", bean.getString());
}
}
use the #DirtiesContext annotation to indicate that the test method may change the state of spring managed beans.