I use TestNG + Spring + hibernate.
When I use transaction in #BeforeClass, I get:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
Code example:
#Transactional(transactionManager = "transactionManager")
#Rollback
#ContextConfiguration(locations = "/WEB-INF/testing/applicationTestContext.xml")
#TestExecutionListeners(listeners = {
ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class
})
public abstract class ExampleOfTest extends AbstractTestNGSpringContextTests{
#Autowired
private SessionFactory sessionFactory;
#BeforeClass
public void setUpClass() {
sessionFactory.getCurrentSession().beginTransaction(); // get HibernateException
sessionFactory.getCurrentSession().getTransaction().commit();
}
....
}
How I can use transaction in #BeforeClass?
I want to use this for one-time data entry used in all class tests.
Problem would be #EnableTransactionManagement should be in your spring context
or
Try something like
// BMT idiom with getCurrentSession()
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work on Session bound to transaction
sessionFactory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
getCurrentSession is like restricted, it should run in a active transaction.
I think this may help.
Related
I'm using Spring + Jpa and I'd like to have EntityManager into my #Configuration class.
Now my class is something like this:
#Configuration
#PropertySource("classpath:base.properties")
public class Config {
private static final Logger log = Logger.getLogger(Config.class);
#Bean
public SpringContextManager contextManager() {
return new SpringContextManager(new DefaultApplication());
}
#Bean(initMethod = "start", destroyMethod = "stop")
public ServerSession serverSession() throws Exception {
try {
ServerSession serverSession = new ServerSession(urlGateway, useSsl, hostGateway, portGateway);
serverSession.setDefaultTimeToLive(5000);
return serverSession;
} catch (Throwable e) {
log.error("", e);
return null;
}
}
#Bean
public PluginManager pluginManager() {
PluginManager pluginManager = new PluginManager();
ThreadLocalManager.set(pluginManager);
return pluginManager;
}
I know that I can't add #PersistenceContext to #Configuration class, so I don't know how to get entityManager at this point.
The goal of this is have entityManager asap the app start because I need to set it into a ThreadLocal class ( i need this class to use entityManager inside a JPA entitylistener where inject of persistenceContext don't work).
Now I'm getting the entityManager from a service annotated with #Service but it would be cleaner to made this settings into #Configuration class. Seems more clean.
Thanks for your help.
I found a nice example to solve my problem. This is the link of the tutorial: link
Consider the typical DBUnit Spring Test (see https://github.com/springtestdbunit/spring-test-dbunit) :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:/META-INF/spring/applicationContext-database.xml",
"classpath:spring-*.xml"
})
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
#DatabaseSetup("/dbunit/data.xml")
public class UnitTest {
#Autowired
private UnitUnderTest uut;
#Test
public void shouldInitDB() {
...
}
}
What I have verified is that, and has expected, Autowiring will happen before DatabaseSetup.
This must happen because DBUnit depends on the application context to provide the configured data source.
The problem is that the UnitUnderTest bean has a #PostConstruct where it loads some data from the DB but, since the Autowiring happens before the DBunit setup, the data will not be available at this stage.
Any ideas on how to solve this issue in a clean way?
You can you Spring's ResourceDatabasePopulator.
I think you can use something like this
#PostConstruct
public void myInMemryPopulator() {
final ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
try {
Resource[] array = resourceResolver.getResources("classpath:/*.sql");
for (Resource resource : array) {
databasePopulator.addScript(resource);
}
databasePopulator.populate(dataSource.getConnection());
} catch (IOException | SQLException e) {
LOGGER.error("Error in databasePopulator {} ", e);
}
}
You can have a setup method in your test class and call the post construct method manually. That will work.
Lazy initialization helped me, I added #Lazy over the #Component* and over the #Autowired in injection points
#Lazy
#Component
public class UnitUnderTestImpl {
...
}
#Lazy
#Autowired
private UnitUnderTest uut;
I'm use JPA (Hibernate 4 vendor) and Spring 3.2.x.
I use this code for get Session and re-attach my detached entity.
Session session = entityManager.unwrap(Session.class);
My code look like this :
#Service
public class SchedulerServiceImpl implements SchedulerService {
#PersistenceContext
private EntityManager entityManager;
#Override
#Transactional
#Scheduled(fixedDelay = 5000)
public void executeTasks() {
.. code ..
while (tasksIterator.hasNext()) {
SchedulerTask readyTask = recalculation(currentTask);
}
.. code ...
}
#Override
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public Deposit recalculation(SchedulerTask schedulerTask) {
boolean asda = entityManager.isOpen(); // get TRUE
Session session = entityManager.unwrap(Session.class); // Exception here
session.update(schedulerTask);
... code ...
}
}
What's wrong?
error :
00:21:52,180 ERROR [org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler]
(pool-10-thread-1) Unexpected error occurred in scheduled task.:
java.lang.IllegalStateException: No transactional EntityManager
available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invokeSharedEntityManagerCreator.java:224)
[spring-orm-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at com.sun.proxy.$Proxy36.unwrap(Unknown Source)
at com.jar.dom.service.SchedulerServiceImpl.recalculation(SchedulerServiceImpl.java:133)
[classes:]
at com.jar.dom.service.SchedulerServiceImpl.executeTasks(SchedulerServiceImpl.java:92)
[classes:]
I solved this adding this lines on spring configuration
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories("com.blablabla")
class ApplicationConfig {
....
}
I have a Spring 3.1 MVC + Hibernate 3.6 project with its junit4 test suit. My problem is that there is no transaction starting in my test cases, even thought I added a #Transactional.
My test case calls a controller and a dao. In the controller, a transaction is started anyway, so it does not complain. In the dao, I added a #Transactional(propagation = Propagation.MANDATORY) to be sure it will take the test's transaction. And currently it raises an IllegalTransactionStateException, which I guess it means there is no current transaction.
I tried to create programmaticaly an transaction and it does work, which means the AOP proxy to get the dao service is not the cause of the problem. However I want to create a transaction with the #Transactional annotation.
here's my dao:
// ...imports...
#Repository("taskDao")
#Transactional(propagation = Propagation.MANDATORY)
public class TaskHome implements TaskDao {
private static final Log log = LogFactory.getLog(TaskHome.class);
private SessionFactory sessionFactory;
#Autowired
public TaskHome(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Task findById(int id) {
log.debug("getting Task instance with id: " + id);
try {
Task instance = (Task) this.sessionFactory.getCurrentSession().get(
Task.class, id); // exception raised here!
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
...
}
Here's my test case:
// ...imports...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
private static ClassPathXmlApplicationContext context;
private static TaskDao taskDao;
#BeforeClass
public static void initHibernate() throws Exception {
context = new ClassPathXmlApplicationContext("applicationContext.xml");
taskDao = context.getBean("taskDao", TaskDao.class);
}
#Test
public void testOnSubmit() {
// expects an existing default transaction here
Task task = taskDao.findById(1); // fails already here
// ... calls the controller and does some tests.
}
}
I searched in all Spring's documentation and googled it in any way I could imagine, but I don't see why the transaction is not started.
Any help is very welcome.
When using #RunWith(SpringJUnit4ClassRunner.class) you should obtain beans from the application context created by SpringJUnit4ClassRunner rather than from your own one.
In your case things go wrong because #Transactional on the unit test creates a transaction in the application context managed by SpringJUnit4ClassRunner, but you call methods on the beans obtained from the application context created manually.
So, remove your #BeforeClass method and obtain TaskDao via autowiring:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
#Autowired
private TaskDao taskDao;
...
}
See also:
9.3.5 Spring TestContext Framework
When i use Junit's org.junit.rules.Timeout with spring's base class AbstractTransactionalJUnit4SpringContextTests, i get this exception:
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
The log output shows:
2010-07-20 09:20:16 INFO [TransactionalTestExecutionListener.startNewTransaction] Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager#6a1fbe]; rollback [true]
2010-07-20 09:20:16 INFO [TransactionalTestExecutionListener.endTransaction] Rolled back transaction after test execution for test context [[TestContext#17b60b6 testClass = MyIntegrationTest, locations = array<String>['classpath:/context.xml', 'classpath:/junit-context.xml'], testInstance = MyIntegrationTest#10a4d7c, testMethod = myTest#MyIntegrationTest, testException = org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress]]
Here is my test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/context.xml", "classpath:/junit-context.xml"})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional
public class MyIntegrationTest extends AbstractTransactionalJUnit4SpringContextTests{
#Rule public Timeout globalTimeout = new Timeout(30000);
#Test
public void myTest() {
// transactional code here saving to the database...
}
}
However whenever i comment out the rule, it all works fine.
How can i marry these two together to work correctly?
Ahh, i worked it out. The way i solved it was to setup the transaction programatically.
#Autowired TransactionManager transactionManager;
#Test
public void test() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
status.setRollbackOnly();
// DO YOUR TEST LOGIC HERE
}
});
}
Hope it helps.
LOL.
You can simply also annotate your test method with #Transactional(timeout = 30) for a 30 second timeout. Which is a lot simpler.