I have below Service:
#Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public void test(String idEntity) throws BaseException
{
getCustomerInformationDAO().updatetm(idEntity);
}
This service has been marked as #Service annotation.
I am calling this service from a controller.
#RequestMapping(value="/test", method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
#Override
public void test(#RequestParam("idEntity") String idEntity) throws BaseException
{
monolithicService.test(idEntity);
}
Below Dao(this has been marked as #Repository) method:
#Override
public void updatetm(String idEntity) throws BaseException
{
updateRecord( "customerinformation-update.updatelfcentitylDt", idEntity );
}
Transaction manager has been marked as
<tx:annotation-driven transaction-manager="transactionManager" />.
With above changes, it doesnt commit the transaction, even if it is successful.
Can anyone help me with this...
I dealt with a similar issue for a whole day.
Just when I was borderline with insanity, I found out that when you use #Transactional in a test the rules are different: by default, your changes are rolled back.
Quick solution: add the annotation #Commit to your method, i.e.:
#Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
#Commit
public void test(String idEntity) throws BaseException
{
getCustomerInformationDAO().updatetm(idEntity);
}
You can read some details in the following text:
One common issue in tests that access a real database is their effect on the state of the persistence store. Even when you use a development database, changes to the state may affect future tests. Also, many operations — such as inserting or modifying persistent data — cannot be performed (or verified) outside of a transaction.
The TestContext framework addresses this issue. By default, the framework creates and rolls back a transaction for each test. You can write code that can assume the existence of a transaction. If you call transactionally proxied objects in your tests, they behave correctly, according to their configured transactional semantics. In addition, if a test method deletes the contents of selected tables while running within the transaction managed for the test, the transaction rolls back by default, and the database returns to its state prior to execution of the test. Transactional support is provided to a test by using a PlatformTransactionManager bean defined in the test’s application context.
If you want a transaction to commit (unusual, but occasionally useful when you want a particular test to populate or modify the database), you can tell the TestContext framework to cause the transaction to commit instead of roll back by using the #Commit annotation.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testing
You probably have two data sources using MapperScan that might be messing up a mybatis config. You need to add SqlSessionFactory and SqlSessionTemplate as mentioned here http://mybatis.org/spring/getting-started.html.
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
#Configuration
#Slf4j
#MapperScan(value = "com.abc.xyx.aaa", sqlSessionTemplateRef = "PrimarySessionTemplate")
public class MyBatisPrimaryConfig {
#Bean(name = "PrimarySessionFactory")
#Primary
public SqlSessionFactory sessionFactory(#Qualifier("PrimaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
#Bean(name = "PrimarySessionTemplate")
#Primary
public SqlSessionTemplate primarySessionTemplate(#Qualifier("PrimarySessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
Related
In our application we are using both MySQL server and Redis databases. we use Redis as a database and not just a cache. we use both of them in a service method and I want to make the method #Transactional to let the spring manages my transactions. Hence if in the middle of an transactional method a RuntimeException is thrown all works on both Redis and MySQL are rolled back. I have followed the spring docs and configured my #SpringBootApplication class as following:
#SpringBootApplication
#EnableTransactionManagement
public class TransactionsApplication {
#Autowired
DataSource dataSource;
public static void main(String[] args) {
SpringApplication.run(TransactionsApplication.class, args);
}
#Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true);
return template;
}
#Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException, IOException {
return new DataSourceTransactionManager(dataSource);
}
}
and this is my service method:
#Service
#RequiredArgsConstructor
#Slf4j
public class FooService {
private final StringRedisTemplate redisTemplate;
private final FooRepository fooRepository;
#Transactional
public void bar() {
Foo foo = Foo.builder()
.id(1001)
.name("fooName")
.email("foo#mail.com")
.build();
fooRepository.save(foo);
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set("foo-mail", foo.getEmail());
}
However after the test method of TestService is called there is no user in MySQL db and I think it's because there is no active transaction for it.Is there any solution for this problem? Should I use spring ChainedTransactionManager class and then how? or I can only manage Redis transactions manually through MULTI?
After playing around with FooService class I found that using #Transactional in a service method that is intended to work with Redis and specially read a value from Redis (which I think most service methods are supposed to read some value from DB) is somehow useless since any read operation would result a null value and that is because Redis queues all operations of a transaction and executes them at the end. Summing up, I think using MULTI and EXEC operations is more preferable since it gives more control to use data in Redis.
After all, any suggestions to use Redis transactions is appreciated.
I am learning Mockito , i have trouble understanding few things. Suppose i want to test a Doa method which gets List of objects and saves it in DB by iterating ove the list . How do test it using Mockito. Below is the code example.
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
#Repository
public class AuditDaoImpl {
#PersistenceContext(unitName = "somepersistance")
EntityManager entityManager;
public <T> boolean saveAuditData(List<T> dataList) {
Iterator<Data> itr = (Iterator<Data>) dataList.iterator();
while (itr.hasNext()) {
Data Data = (Data) itr.next();
Audit audit = new Audit();
audit.setUserName(Data.getUserName());
entityManager.persist(audit);
}
return true;
}
}
Assuming that your test class is annotated and running with spring (by using #RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration("classpath:applicationContext.xml")) annotation and you have this working. And if your main concern is to verify that entityManager.persist(audit); is invoked for each element, something like this should work
#Autowired //Autowired to get mockito to inject into the spring-handled dao
#InjectMocks
AuditDaoImpl auditDao;
#Mock
EntityManager entityManager;
#Test
public void saveAllAudit_entityManagerShouldPersistAll() {
List<Data> dataList = new ArrayList<>();
dataList.add(new Data("username"));
//add more to list
auditDao.saveAuditData(dataList);
verify(entityManager, times(1)).persist(anyObject());
}
If you actually need to test that it is persisted correctly, an in-memory database would be the way to go.
Mockito is poor for testing cases that deal with the persistence layer. You should use an embedded container to test the persistance layer. An embedded container is an in memory database that simulates your Database and is fast to create, making it ideal for unit tests.
Look at this SO question and read the SECOND answer :
Testing an EJB with JUnit
I had an issue with spring's #Order annotation, it seems i can't get it to work in my application. Hence I managed to create a test class that imitates the very same behaviour which #Order does not have any effect on my components. The following test fails to run because of lack of bean typed javax.sql.Datasource:
package com.so;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class TestSpring {
public static void main(String[] args) {
Class<?>[] classes = new Class[]{AConf.class, ADAO.class, AService.class, RepoConf.class} ;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
}
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 100)
public static class AConf {
#Autowired
AService aService;
}
#Repository
#Order(Ordered.LOWEST_PRECEDENCE)
public static class ADAO {
#Autowired
#Qualifier("myds")
DataSource dataSource;
}
#Service
#Order(Ordered.LOWEST_PRECEDENCE)
public static class AService {
#Autowired
ADAO adao;
#PostConstruct
public void init() {
System.out.println("service init");
}
}
// #Component does not have any effect
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public static class RepoConf {
#Autowired
BeanFactory beanFactory;
#PostConstruct
public void init() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
configurableBeanFactory.registerSingleton("myds", new AbstractDataSource() {
#Override
public Connection getConnection() throws SQLException {
return null;
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
});
}
}
}
Manual bean registration has risks as stated here: https://stackoverflow.com/a/11751503/1941560, although I cannot find out in which circumtances that #Order annotation works. For above configuration of application I expect the execution order like; RepoConf, AConf, ADAO, AService.
A weird thing to notice is that when I changed the order of component classes declared to (with commencing array with RepoConf):
Class<?>[] classes = new Class[]{RepoConf.class, AConf.class, ADAO.class, AService.class};
or changed my AConf class to:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 100)
public static class AConf {
#Autowired
RepoConf repoConf; // must be declared before aService
#Autowired
AService aService;
}
application works as expected. Could someone explain that spring container's behaviour and how can I utilize #Order annotations?
springframework version I use is 4.2.1.RELEASE
Judging by the JavaDoc documentation for the #Order annotation, I don't think it is used to order bean creation:
NOTE: Annotation-based ordering is supported for specific kinds of
components only — for example, for annotation-based AspectJ aspects.
Ordering strategies within the Spring container, on the other hand, are
typically based on the Ordered interface in order to allow for
programmatically configurable ordering of each instance.
Consulting the Spring Framework documentation, the #Ordered annotation seems to be used for:
Ordering of instances when injected into a collection
Ordering the execution of event listeners
Ordering of #Configuration class processing, for example if you want to override a bean by name.
From the documentation, it does not look like #Order is intended for your use case.
However, from the Spring documentation, we can see there depends-on, which enforces that certain beans should be created before the bean being defined. This has a corresponding annotation #DependsOn.
It looks that your version of Spring Framework simply ignores the #Order annotation on configuration classes. There's no surprise here, because that annotation should only be used on classes that implement the Ordered interface. Moreover, I could never find any reference to it about configuration classes in Spring Framework reference documentation.
Anyway you are walking in terra incognita here. It is not described in official documentation, so it will work or not depending on implementation details. What happens here is (seeing the results):
the AnnotationConfigApplicationContext first instantiate the configuration classes and their beans in their declaration order
it then builds all the beans in their instantiation order
As you register the datasource myds at build time in its configuration class, it happens to be registered in time to be autowired in other beans when repoConf is built before any other beans using them. But this is never guaranteed by Spring Framework documentation and future versions could use a different approach without breaking their contract. Moreover, Spring does change initialization order to allow dependencies to be constructed before beans depending on them.
So the correct way here is to tell Spring that the ADAO bean depends on the configuration RepoConf. Just throw away all Order annotations which are no use here, and put a #DependsOn one. You code could be:
package com.so;
...
public class TestSpring {
public static void main(String[] args) {
Class<?>[] classes = new Class[]{AConf.class, ADAO.class, AService.class, RepoConf.class} ;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
}
#Configuration
public static class AConf {
#Autowired
AService aService;
}
#DependsOn("repoConf")
#Repository
public static class ADAO {
#Autowired
#Qualifier("myds")
DataSource dataSource;
}
#Service
public static class AService {
#Autowired
ADAO adao;
#PostConstruct
public void init() {
System.out.println("service init");
}
}
#Configuration("repoConf")
public static class RepoConf {
#Autowired
BeanFactory beanFactory;
#PostConstruct
public void init() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
configurableBeanFactory.registerSingleton("myds", new AbstractDataSource() {
#Override
public Connection getConnection() throws SQLException {
return null;
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
});
}
}
}
#DependsOn ensures that repoConf will be created before ADAO making the datasource available for dependency injection.
I'm using Spring 3.1.2 with Hibernate 4.
I have a DAO implementation class MyDaoImpl annotated with #Repository so that exception translation is enabled. I have a service class MyService annotated with #Transactional as follows:
public class MyService implements IMyService {
private MyDao myDao;
#Autowired
public void setMyDao(MyDao dao) {
this.myDao = dao;
}
#Override
#Transactional
public void createA(String name)
{
A newA = new A(name);
this.myDao.saveA(newA);
}
}
I've wrote a unit tests class MyServiceTest as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:beans.xml" })
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class MyServiceTest implements IMyServiceTest {
private IMyService myService;
private SessionFactory sessionFactory;
#Autowired
public void setMyService(IMyService myService)
{
this.myService = myService;
}
#Autowired
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
#Test
#Override
public void testCreateA()
{
//Assume that there is already a row of table A with the name "A1"
//so I expect to get a Spring DataAccessException (or subtypes)
//when the session is flushed
this.myService.createA("A1");
this.sessionFactory.getCurrentSession().flush();
//asserts
}
}
When I run the test, I get a Hibernate specific exception ConstraintViolationException. I've found on the forum that this is because the translation system takes place outside the transaction, so in this case after testCreateA() returns. I don't know if this is the real cause, but if it is, it means that I can't test that the translation works for my DAOs. One solution would be to remove the #Transactional annotations from my unit tests, but I would no benefit from the rollback feature.
What are your recommendations?
EDIT: I've added the SessionFactory declared in my context to the test class, so that I can access the current session for flushing.
Some additional explanations: In this case, I get the exception when the session is flushed (which is inside the transaction). I flush the session in order to avoid false positives as it is explained in the docs. Also, since the default propagation is REQUIRED, the testCreateA() transaction is also used for the call to createA(), so the changes are not flushed (generally) until testCreateA() returns.
Have you added PersistenceExceptionTranslationPostProcessor bean defination? Like
<!--
Post-processor to perform exception translation on #Repository classes
(from native exceptions such as JPA PersistenceExceptions to
Spring's DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
From Spring doc.
Bean post-processor that automatically applies persistence exception
translation to any bean that carries the #Repository annotation, adding
a corresponding PersistenceExceptionTranslationAdvisor to the exposed
proxy (either an existing AOP proxy or a newly generated proxy that
implements all of the target's interfaces).
Translates native resource exceptions to Spring's DataAccessException
hierarchy. Autodetects beans that implement the
PersistenceExceptionTranslator interface, which are subsequently asked
to translate candidate exceptions
I have no problem testing my DAO and services, but when I test INSERTs or UPDATEs I want to rollback the transaction and not effect my database.
I'm using #Transactional inside my services to manage transactions. I want to know, is it possible to know if a transaction will be fine, but rollback it to prevent altering database?
This is my Test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/META-INF/spring.cfg.xml")
#TransactionConfiguration(defaultRollback=true)
public class MyServiceTest extends AbstractJUnit38SpringContextTests {
#Autowired
private MyService myService;
#BeforeClass
public static void setUpClass() throws Exception {
}
#AfterClass
public static void tearDownClass() throws Exception {
}
#Test
public void testInsert(){
long id = myService.addPerson( "JUNIT" );
assertNotNull( id );
if( id < 1 ){
fail();
}
}
}
The problem is that this test will fail because transaction was rollbacked, but the insert is OK!
If I remove #TransactionConfiguration(defaultRollback=true) then the test pass but a new record will be inserted into database.
#Test
#Transactional
#Rollback(true)
public void testInsert(){
long id = myService.addPerson( "JUNIT" );
assertNotNull(id);
if( id < 1 ){
fail();
}
}
Now can test pass correctly, but rollback is ignored and the record is inserted into the database.
I have annotated the method addPerson() inside myService with #Transactional, obviously.
Why is the rollback being ignored?
You need to extend transaction boundaries to the boundaries of your test method. You can do it by annotating your test method (or the whole test class) as #Transactional:
#Test
#Transactional
public void testInsert(){
long id=myService.addPerson("JUNIT");
assertNotNull(id);
if(id<1){
fail();
}
}
You can also use this approach to ensure that data was correctly written before rollback:
#Autowired SessionFactory sf;
#Test
#Transactional
public void testInsert(){
myService.addPerson("JUNIT");
sf.getCurrentSession().flush();
sf.getCurrentSession().doWork( ... check database state ... );
}
check out
http://static.springsource.org/spring/docs/2.5.x/reference/testing.html
Section 8.3.4 in particular
Spring has some classes for testing that will wrap each test in a transaction, so the DB is not changed. You can change that functionality if you want too.
Edit -- based on your more infos, you might want to look at
AbstractTransactionalJUnit38SpringContextTests at
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/context/junit38/AbstractTransactionalJUnit38SpringContextTests.html
Use Following annotation before class :
#TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
#Transactional
here txManager is application context's Transaction Manager.
Here txManager is an instance or bean id of Transaction manager from application context.
<!-- Transaction Manager -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
Add your code inside setUp() method, this will execute in start of the test and the last wrap up code should be put in teatDown() method that will executed at last. or you can also use #Before and #After annotation instead of it.
If your
myService.addPerson("JUNIT");
method is annotated like #Transactional you will be getting some different kind or errors trying to fix this. So you better just test DAO methods.