Search code examples
javaunit-testingjpaeager-loading

Why FetchType.EAGER doesn't work in unit tests?


My application with JPA (Hibernate) and Spring. My enitites are:

Department

@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<>();
}

User

@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;

}

DAO for 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();
    }
}

Whan I run the code in Main class it joins the entities:

Main

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']}]

Whan I run a unit test with the same code, the department has empty list of users:

@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 = []}]
    }
}

So, my question is: why EAGER works in Main, but in test it doesn't?


Solution

  • I assume that you are not adding the new User to the list of users in the Department.

    Your constructor must look like:

    public User(
                // other fields, 
                Department department) {
       // set all fields.
    
       department.getUsers().add(this);
    }
    

    In the Main it's working because you have two transactions and in the reading is in a separate transaction and the Department is fetched from DB.

    But in the Unit Test it returns the Department from the memory where the users list is empty.

    It's very important to keep your relationships set correctly before saving entities.