Почему FetchType.EAGER не работает в модульных тестах?

Мое приложение с JPA (Hibernate) и Spring. Мои энититы:

отделение

@Entity
@Table(schema = "myschema")
public class Department {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(columnDefinition = "BINARY(16)", length = 16 )
    private UUID uuid;

    @Column(name = "name", length = 200, nullable = false)
    private String name;

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
    List<User> users = new ArrayList<>();
}

Пользователь

@Entity
@Table(schema = "myschema")
public class User {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(columnDefinition = "BINARY(16)", length = 16 )
    private UUID uuid;

    @NotNull
    @Column(name = "login", length = 100, nullable = false)
    private String login;

    @ManyToOne
    @JoinColumn(name = "department_fk", nullable = false)
    private Department department;

}

ДАО для отдела:

@Repository("departmentDao")
public class DepartmentDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public UUID save(Department entity) {
        log.info("Department entity will be persisted", entity);
        entityManager.persist(entity);
        return entity.getUuid();
    }

    @Transactional
    public List<Department> getAllWithUsers() {
        log.info("Select all departments");
        CriteriaQuery<Department> criteria = entityManager.getCriteriaBuilder().createQuery(Department.class);
        Root<Department> root = criteria.from(Department.class);
        criteria.select(root);
        return entityManager.createQuery(criteria).getResultList();
    }
}

Когда я запускаю код в основном классе, он объединяет сущности:

Главный

ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
DepartmentDao departmentDao = (DepartmentDao) context.getBean("departmentDao");

Department newDep = new Department("WebDepartment", new Date());
departmentDao.save(newDep);

User newUser1 = new User("Test1", newDep);
User newUser2 = new User("Test2", newDep);
userDao.save(newUser1);
userDao.save(newUser2);

List<Department> deps = departmentDao.getAllWithUsers();

//deps
//[Department{name='WebDepartment', users = ['Test1', 'Test2']}]

Когда я запускаю модульный тест с тем же кодом, у отдела есть пустой список пользователей:

@ContextConfiguration(locations = "classpath:META-INF/spring-test.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class DaoTests {

    @Autowired
    private DepartmentDao departmentDao;

    @Autowired
    private UserDao userDao;

    @Test
    @Transactional
    public void daoTests() throws NoSuchAlgorithmException {

        Department newDep = new Department("WebDepartment", new Date());
        departmentDao.save(newDep);

        User newUser1 = new User("Test1", newDep);
        User newUser2 = new User("Test2", newDep);
        userDao.save(newUser1);
        userDao.save(newUser2);
        List<Department> deps = departmentDao.getAllWithUsers();

        //deps
        //[Department{name='WebDepartment', users = []}]
    }
}

Итак, мой вопрос: почему ЖАЖДУЩИЙ работает в Main, а в тесте - нет?

Разница заключается в том, что ваш модульный тест выполняется внутри транзакции.

Twister 18.02.2019 08:34

Я создаю пользовательские сущности внутри одной транзакции и пытаюсь получить их внутри другой транзакции? Я правильно вас понял?

Eugene_Z 18.02.2019 08:59

Не могли бы вы показать конструктор объекта User?

Simon Martinelli 18.02.2019 10:44
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
370
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я предполагаю, что вы не добавляете нового пользователя в список пользователей в отделе.

Ваш конструктор должен выглядеть так:

public User(
            // other fields, 
            Department department) {
   // set all fields.

   department.getUsers().add(this);
}

В Main это работает, потому что у вас есть две транзакции, а при чтении это отдельная транзакция, а отдел извлекается из БД.

Но в Unit Test возвращает отдел из памяти, где список пользователей пуст.

Перед сохранением объектов очень важно правильно настроить отношения.

Да, мне помогло. Спасибо! Но еще один вопрос. Создает ли сохранение в БД или чтение из БД в модульных тестах реальные транзакции? Или все манипуляции с данными внутри java памяти?

Eugene_Z 18.02.2019 13:13

Если вы используете Transactional в модульном тесте с SpringRunner, он создает транзакцию базы данных и по умолчанию выполняет откат в конце метода тестирования.

Simon Martinelli 18.02.2019 13:48

Другие вопросы по теме