I have a problem while run test class complexly, but not each method separately. Previously ORM was based on JDBCTemplate, I changed it to Hibernate. So when i run test class all methods make changes in database, but don't rolling them back.
Test class:
package ua.com.foxminded.dao;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import ua.com.foxminded.config.SpringDaoTestConfig;
import ua.com.foxminded.model.Course;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = SpringDaoTestConfig.class)
@Transactional
@Rollback
class CourseDaoIT {
private static final int GENERATED_COURSES_COUNT = 3;
@Autowired
private CourseDao courseDao;
private Course expectedCourse;
@Test
void create_shouldCreateCourse() {
assertEquals(GENERATED_COURSES_COUNT, courseDao.getAll().size());
expectedCourse = new Course(1L, 2021);
Course actualCourse = courseDao.create(expectedCourse);
assertEquals(expectedCourse, actualCourse);
}
@Test
void getById_shouldReturnCourse() {
assertEquals(GENERATED_COURSES_COUNT, courseDao.getAll().size());
Course course1 = courseDao.create(new Course(2021));
Course course2 = courseDao.create(new Course(2022));
Course course3 = courseDao.create(new Course(2023));
expectedCourse = course2;
Optional<Course> actualCourse = courseDao.getById(expectedCourse.getId());
assertTrue(actualCourse.isPresent());
assertEquals(expectedCourse, actualCourse.get());
}
@Test
void getAll_shouldReturnAllCourses() {
assertEquals(GENERATED_COURSES_COUNT, courseDao.getAll().size());
Course course1 = courseDao.create(new Course(2021));
Course course2 = courseDao.create(new Course(2022));
Course course3 = courseDao.create(new Course(2023));
List<Course> expectedCourses = Arrays.asList(course1, course2, course3);
List<Course> actualCourses = courseDao.getAll().stream().skip(GENERATED_COURSES_COUNT).collect(Collectors.toList());
assertEquals(expectedCourses, actualCourses);
}
@Test
void delete_shouldDeleteCourse() {
assertEquals(GENERATED_COURSES_COUNT, courseDao.getAll().size());
Course course = courseDao.create(new Course(2021));
assertEquals(GENERATED_COURSES_COUNT 1, courseDao.getAll().size());
courseDao.delete(course.getId());
assertEquals(GENERATED_COURSES_COUNT, courseDao.getAll().size());
}
@Test
void update_shouldUpdateCourse() {
assertEquals(GENERATED_COURSES_COUNT, courseDao.getAll().size());
Course course = courseDao.create(new Course(2021));
int randomEstablishYear = course.getEstablishYear() 285;
assertEquals(GENERATED_COURSES_COUNT 1, courseDao.getAll().size());
course.setEstablishYear(randomEstablishYear);
courseDao.update(course);
Optional<Course> updatedCourse = courseDao.getById(course.getId());
assertTrue(updatedCourse.isPresent());
assertEquals(updatedCourse.get(), course);
}
}
Dao class:
package ua.com.foxminded.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.GenericTypeResolver;
import ua.com.foxminded.exception.ClassNotFoundException;
import javax.persistence.OptimisticLockException;
import java.util.List;
import java.util.Optional;
/**
* DAO class AbstractDao
*
* @param <T> the type of ua.com.foxminded.model package classes
*/
public abstract class AbstractDao<T> {
/**
* Property - logger to log important actions
*/
private static final Logger logger = LoggerFactory.getLogger(AbstractDao.class.getName());
/**
* Property - generic type
*/
private final Class<T> genericType;
/**
* Property - session factory
*/
private final SessionFactory sessionFactory;
/**
* Property - simple class name
*/
private final String simpleClassName;
/**
* Constructor autowired by SessionFactory bean.
*
* <p> Defines session factory, using Spring
*
* <p> Defines child class type, using GenericTypeResolver
*
* <p> Defines simple class name, using generic type for logger
*
* @param sessionFactory autowired SessionFactory bean
* @throws ClassNotFoundException if class of described by AbstractDao value is not found
* @see GenericTypeResolver#resolveTypeArgument(Class, Class)
*/
@Autowired
@SuppressWarnings("unchecked")
protected AbstractDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
Class<?> classType = GenericTypeResolver.resolveTypeArgument(getClass(), AbstractDao.class);
this.genericType = (Class<T>) classType;
if (genericType == null) {
throw new ClassNotFoundException("Generic type in AbstractDao is null");
}
this.simpleClassName = this.genericType.getSimpleName();
}
/**
* Returns created object in table mapped by value class type
*
* @param value value to create
* @return non-null value with defined id, described by AbstractDao
*/
public T create(T value) {
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
logger.debug("Creating new {} object", simpleClassName);
value = genericType.cast(session.merge(value));
logger.debug("{} object has been created", simpleClassName);
session.getTransaction().commit();
return value;
}
}
/**
* If a value with param id is present in table mapped by this class type,
* returns an Optional describing the value, otherwise throws NullPointerException
*
* @param id id of searching in database object
* @return an Optional describing the value of this Optional,
* if a value with param id is present in table mapped by this class type, and the value
* matches the given predicate, otherwise returns null
* @throws NullPointerException if value with param id is not present in database
*/
public Optional<T> getById(long id) {
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
logger.debug("Getting {} object by id = {}", simpleClassName, id);
T obtainedObject = session.get(genericType, id);
logger.debug("{} object with id ={} has been obtained", simpleClassName, id);
session.getTransaction().commit();
return Optional.of(obtainedObject);
}
}
/**
* Returns a list of all values of type described this AbstractDao in mapped table of a database
*
* @return a list of all values of type described this AbstractDao in mapped table of a database
*/
public List<T> getAll() {
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
logger.debug("Getting all {} objects", simpleClassName);
List<T> obtainedObjects = session.createQuery(
"SELECT a FROM " simpleClassName " a", genericType).getResultList();
logger.debug("All objects {} have been obtained", simpleClassName);
session.getTransaction().commit();
return obtainedObjects;
}
}
/**
* Deletes object with param id from the table mapped by the class described in AbstractDao,
* if object with such param id is present in database, otherwise throws EntityNotFoundException
*
* @param id id of deleting from database object
* @throws ua.com.foxminded.exception.EntityNotFoundException if object with such param id is not present in database
*/
public void delete(long id) {
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
logger.debug("Deleting {} object with id = {}", simpleClassName, id);
T value = session.load(genericType, id);
session.delete(value);
logger.debug("{} object with id = {} has been deleted", simpleClassName, id);
session.getTransaction().commit();
}
}
/**
* Updates object in table mapped by class type if object with id defined in param value is present,
* otherwise throws OptimisticLockException
*
* @param value value to update object in database with such value's id
* @throws OptimisticLockException if object with id defined in param value
* is not present in database
*/
public void update(T value) {
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
logger.debug("Updating {} object", simpleClassName);
session.update(value);
logger.debug("{} object has been updated", simpleClassName);
session.getTransaction().commit();
}
}
}
I was trying to change CRUD methods in DAO. Some don't help. Others break all project. I think it has a smarter solution.
CodePudding user response:
You're using sessionFactory.openSession()
and manually starting and committing transactions with Hibernate.
So those transactions are not managed by Spring.
To use Spring-managed transactions with Hibernate, use Spring's LocalSessionFactoryBean
in XML config or LocalSessionFactoryBuilder
in Java Config.
Then, in your DAO/repository implementation use sessionFactory.getCurrentSession()
to get the current Spring-managed Session
instead of opening a new Session
, and remove all usage of session.beginTransaction()
and session.getTransaction().commit()
.
You'll also need to use Spring's HibernateTransactionManager
.
See the Hibernate Transaction Setup and Hibernate sections of the Spring Framework reference manual for details.
CodePudding user response:
I changed my DataConfig file to this ConfigFile and set @Transactional(propagation = Propagation.REQUIRED) in my Repository (AbstractDao)