I'm running JUnit tests using spring-test, my code looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {})
#Transactional (propagation = Propagation.REQUIRED)
#Rollback
public class MyTest {
#Autowired
private MyRepository repository;
#Before
public void before() {
//clean repository
}
#Test
public void test_1() {
//add new entity
}
#Test
public void test_2() {
//add new entity
}
...
}
I want to rollback my db in state before all tests. And tables rollbacks but sequence for id generation increases with each test.
Please help me to find the way to set sequence in start value before each test.
I'm using Spring, Hibernate, HsqlDb
If you call a method with #Transactional you can't make a rollback. What you can do is an #After in order to drop all the database, and in a #Before create it again. I think it's not a very good idea, you should drop the values in order to isolate each test. I recommend you to use H2 database in order to be able to use in memory storage, so you don't have to worry about the id number.
You have to add this dependency to the pom:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.193</version>
</dependency>
And the configuration inside the .xml should be something like this:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">jdbc:h2:mem:testDB;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1</property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<property name="show_sql">false</property>
<property name="hbm2ddl.auto">create</property>
</session-factory>
</hibernate-configuration>
Of course you should adapt it to your system, but you must keep that username and password in order to connect successfully.
If you don't want to auto create the tables, you could add this parameter on the connection URL:
INIT=RUNSCRIPT FROM 'classpath:scripts/create.sql
and this will run your script before the tests.
Then in your tests you could do something link this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {})
#Transactional (propagation = Propagation.REQUIRED)
public class MyTest {
#Autowired
private MyRepository repository;
#Test
public void test_1() {
//add new entity
}
#Test
public void test_2() {
//add new entity
}
#After
public void tearDown(){
repository.clean();
}
}
And the clean method could be something like this
public void clean(){
for (T obj: this.findall()) //replacing T for the type if you don't use generics
session.delete(obj); //your entity manager or session, depending on how you do the queries
}
Related
I am currently facing a weird issue with a plain Java EE application on WildFly 25 for which I don't find the root cause. I've been using similar configurations for a few customers and never had any issue with the code. I know for transactions to work, I need to inject everything involved properly, use the #Stateless annotation and work with the #Transactional annotation on methods which need it. But I never had the issue that I just don't get any transaction, and I am somewhat lost right now. The datasource was also configured with JTA set to true.
My repository:
#Stateless
public class DocumentImportLogRepository{
#PersistenceContext(unitName = "AktenimportPU")
EntityManager em;
public <T> Object find(Class<T> entityClass, Object primaryKey)
{
return em.find(entityClass, primaryKey);
}
public void persist(Object object)
{
em.persist(object);
}
public void forcePersist(Object object)
{
em.persist(object);
em.flush();
}
public void merge(Object object)
{
em.merge(object);
}
public void forceMerge(Object object)
{
em.merge(object);
em.flush();
}
public void remove(Object object)
{
em.remove(object);
}
is called within the following service class:
#Stateless
public class DocumentImportService
[...]
#Inject
DocumentImportLogRepository importLogRepo;
from several methods all originating from:
#Transactional
public void doImport()
{
[...]
readInputFolder(Config.DOCUMENT_IMPORT_FOLDER);
prepareImport(importLogRepo.getByState(State.PARSED), getPersonalakten());
performArchive(importLogRepo.getByState(State.PREPARED));
performArchiveMove(importLogRepo.getByState(State.ARCHIVED));
[...]
}
which is triggered by a controller:
#Named("StartController")
#ApplicationScoped
public class StartController implements Serializable {
#Inject
private transient DocumentImportService importService;
[...]
#Transactional
#TransactionTimeout(value=120, unit = TimeUnit.MINUTES)
public void performTask(Task task)
{
[...]
switch(task)
{
case Personalaktenimport:
importService.doImport();
break;
}
[...]
}
the actual method call failing:
#Transactional
public void readInputFolder(Path inputFolder) throws IOException
{
[...] importLogRepo.forcePersist(entry); [...]
}
with the exception:
javax.ejb.EJBTransactionRolledbackException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context
persistence.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="AktenimportPU">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/datasources/Aktenimport</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.jdbc.time_zone" value="Europe/Berlin"/>
</properties>
</persistence-unit>
</persistence>
I finally found out what caused this error:
I wasn't really reading the exceptions prior since I thought that they are just commonly caught mistakes in the inputs, but yeah, I had several exceptions caught, however, javax.validation.ValidationException wasn't one of them. The mistake was that the entity had a field BARCODE whose type was an enumeration BARCODE_TYPE which contains a set of predefined values. My intention was to just let it run into an error but continue the import on unknown types, however, if this exception appears it seems to set the transaction into an error state from which the application cannot recover. Removing the #NotNull annotation on the enum field did get rid of the errors.
Let's say we have the next test:
#ContextConfiguration(classes = {MyDaoContext.class})
public class MyDaoTest extends AbstractTransactionalTestNGSpringContextTests {
#Autowired
private MyDao dao;
#Test
public void insertDataTest() {
// insert data here and test something
}
#Test(dependsOnMethods = "insertDataTest")
public void modifyPreviouslyInsertedDataTest() {
// get previously inserted data and test something
}
}
Second test will fall because when we have finished first test the inserted data are gone.
Is there a way to rollback a transaction after all tests have finished their work?
Each test runs in its own transaction and rollbacks at the end. You can tune that by adding #Rollback(false) to your first test.
The problem now is that the transaction of insertDataTest has completed so you should remove what it has created manually. To do that, the class you extends from has several utility methods such as deleteFromTables or deleteFromTableWhere.
This should go ideally in an #AfterClass or something.
But that's not what I would do. I would factor out the data that are inserted by insertDataTest in a shared utility. That way, you could call it again in your second test and remove the dependsOnMethods. Having such dependency is not recommended as you can't run that test in isolation.
Try the following (which would work in JUnit, I'm not sure about TestBG)
#ContextConfiguration(classes = {MyDaoContext.class})
public class MyDaoTest extends AbstractTransactionalTestNGSpringContextTests {
#Autowired
private MyDao dao;
#Test
#Rollback(false)
public void insertDataTest() {
// insert data here and test something
}
#Test(dependsOnMethods = "insertDataTest")
public void modifyPreviouslyInsertedDataTest() {
// get previously inserted data and test something
}
}
In that case of course you would have to delete the data from the first method manually
I have class CandidateService marked as #Transactional
#Transactional
#Service("candidateService")
public class CandidateService {
#Autowired
private CandidateDao candidateDao;
....
public void add(Candidate candidate) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String login = auth.getName();
User user = utilService.getOrSaveUser(login);
candidate.setAuthor(user);
candidateDao.add(candidate);
}
...
}
dao implementation:
#Override
public Integer add(Candidate candidate) throws HibernateException{
Session session = sessionFactory.getCurrentSession();
if (candidate == null) {
return null;
}
Integer id = (Integer) session.save(candidate);
return id;
}
if I write in #controller class:
#RequestMapping("/submitFormAdd")
public ModelAndView submitFormAdd(
Model model,
#ModelAttribute("myCandidate") #Valid Candidate myCandidate,
BindingResult result,
RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
return new ModelAndView("candidateDetailsAdd");
}
myCandidate.setDate(new Date());
candidateService.add(myCandidate);
.....
}
After executing this methods data put to database!
if I write test:
#ContextConfiguration(locations = {"classpath:/test/BeanConfig.xml"})
public class CandidateServiceTest extends AbstractTransactionalJUnit4SpringContextTests{
#Autowired
CandidateService candidateService;
#BeforeClass
public static void initialize() throws Exception{
UtilMethods.createTestDb();
}
#Before
public void setup() {
TestingAuthenticationToken testToken = new TestingAuthenticationToken("testUser", "");
SecurityContextHolder.getContext().setAuthentication(testToken);
}
#After
public void cleanUp() {
SecurityContextHolder.clearContext();
}
#Test
public void add(){
Candidate candidate = new Candidate();
candidate.setName("testUser");
candidate.setPhone("88888");
candidateService.add(candidate);
List<Candidate> candidates = candidateService.findByName(candidate.getName());
Assert.assertNotNull(candidates);
Assert.assertEquals("88888", candidates.get(0).getPhone());
}
}
test is green but after executing I don't see data in my database.
Can you explain me why and how to fix it?
UPDATE
configuration:
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Менеджер транзакций -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
above your test class will not affect to database.
set or add defaultRollback = false to see data persisted in table.
I think you should add TransactionConfiguration annotation with param defaultRollback = false
After completion of test method the changes are rolled back. Test method database changes are reverted and you can do the other test with new data.
You no need to worry about the model object with same id in different test cases.
If you need common data you can do with in setup() method that changes also not saved in the database.
Before executing test methods the setup() method will be called. if you have 3 test method then 3 times setup() method will be called.
sorry for my bad English.......
I understand we need to keep #Transactional boundaries as short as possible. Here is the code :
I am using userDAO object through Spring dependency injection :
private static ApplicationContext context ;
private UserDAO userDAO;
public TransactionsTest() {
userDAO = (UserDAO) context.getBean("userDAO");
}
I am calling testSaveUserAccounts() from TransactionsTest class trying to use userDAO for insertion/updation of data.
Case 1:
#Transactional
public void testSaveUserAccounts() {
UserAccounts userAccounts = new UserAccounts();
userAccounts.setCommunityId(10L);
userDAO.saveObject(userAccounts);
}
// This method is inside UserDAO
public void saveObject(Object object) {
entityManager.merge(object);
}
Case 2:
#Transactional
public void testSaveUserAccounts() {
UserAccounts userAccounts = new UserAccounts();
userAccounts.setCommunityId(10L);
userDAO.saveObject(userAccounts);
}
// This method is inside UserDAO
#Transactional(propagation=Propagation.REQUIRED)
public void saveObject(Object object) {
entityManager.merge(object);
}
Spring Context :
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceLocal" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceUnitName" value="spring-jpa" />
</bean>
UserDAO :
#Repository
public class UserDAO extends BaseDAO {
#Transactional(propagation=Propagation.REQUIRED)
public void saveObject(Object object) {
entityManager.merge(object);
}
}
BaseDAO :
public abstract class BaseDAO {
protected EntityManager entityManager;
protected HashMap<String,Long> eventIdMap = new HashMap<String,Long>();
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this. entityManager = entityManager;
}
public <T> T getById(final Class<T> clazz, final Serializable id) {
T object = clazz.cast(entityManager.find(clazz, id));
return object;
}
#Transactional
public Object save(Object ob) {
Object object = entityManager.merge(ob);
return object;
}
#Transactional
public void persist(Object ob) {
entityManager.persist(ob);
}
#SuppressWarnings("unchecked")
public <T> ArrayList<T> getAll(final Class<T> clazz) {
String hqlQuery = "from "+ clazz.getSimpleName();
ArrayList<T> list = (ArrayList<T>)entityManager.createQuery(hqlQuery).getResultList();
return list;
}
}
I have been experimenting around several transactional boundaries REQUIRED, REQUIRES_NEW, SUPPORTS, etc but couldn't confidently make out as to why Case 1 (when method2 is called which is inside transactional boundary of method1) does not merges the data, while, this is solved in Case 2.
Why do I need to specify #Transactional in inner methods as well when already I have marked calling function within Transaction boundary ?
Your transaction test class is not a Spring Bean that is why Case 1 does not work. Spring needs to detected that a method has #Transactional on it and it does that when spring registers the bean with the spring bean factory.
Also keep in mind that the if you are doing Proxy Based AOP calls within the same bean will not be caught by the transaction aspect unless you use AspectJ load time weaving or AspectJ compile time weaving.
Also putting #Transactional on your Dao's is not a really a good idea because transaction boundaries are best marked at the service layer. The reason why is that a particular service method might need to interact with multiple Dao's and you would want those Dao's actions to be part of the tx started by the service layer, rather than having to analyze the Dao's to see what the propagation behavior is.
Can you post the complete code for the test class?
#Transactional does nothing locally, it only has an effect when called from a different service. In other words, you have to leave your current context for transaction annotations to do anything. so calling method 1 is identical in both cases, Case 2 only does anything if method2 is called from another service.
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.