首页 文章

JUnit hibernate:测试提取的懒惰关联

提问于
浏览
0

我有一个带有延迟加载的网站集的用户模型:

@Entity
public class User {

    @Id
    @GeneratedValue
    private Integer id;

    @OneToMany(fetch = FetchType.LAZY)
    private Set<Site> sites;

    ...

我的UserDao中的HQL查询通过id加载用户,并且还提取关联的站点:

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Query;
import org.springframework.stereotype.Repository;

@Repository
public class userDaoImpl implements UserDao() {

    @Inject
    private SessionFactory sessionFactory;

    public Session getCurrentSession() {
        return sessionFactory.getCurrentSession();
    }

    public User findByIdFetchSites(final Integer id) {
        String queryString = "SELECT user FROM User user LEFT JOIN FETCH user.sites WHERE user.id = :id";
        Query qry = getCurrentSession().createQuery(queryString).setInteger("id", id);
        return qry.uniqueResult();
    }

    ...

我想在运行这个Dao方法时测试网站是否与用户一起被提取 . 我有一个针对HSQLDB运行的JUnit测试类,如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:/test-context.xml" })
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
@Transactional
public class UserDaoImplTest {

@Inject private UserDaoImpl userDaoImpl;

@Test
public void retrievesUserFetchesSites() {
    Set<Site> sites = new HashSet<Site>();
    ...

    User user = new User();
    user.setSites(sites);
    userDaoImpl.saveOrUpdate(user); 

    User retrievedUser = userDaoImpl.findByIdFetchSites(1);

    assertTrue(retrievedUser.getSites().containsAll(sites));
}

其中@Transactional是spring transactional annotation(org.springframework.transaction.annotation.Transactional) . 即使我在一个简单的findUserById DAO方法上运行它,测试也会通过,对于这个方法,没有显式获取站点:

public User findById(final Integer id) {
    String queryString = "SELECT user FROM User user WHERE user.id = :id";
    Query qry = getCurrentSession().createQuery(queryString).setInteger("id", id);
    return qry.uniqueResult();
}

我不完全确定为什么在断言之前提取站点(我在日志中的select语句中看不到它),但我想我想要做的是在断言发生之前关闭当前会话;就像是:

@Test
public void retrievesUserFetchesSites() {
    Set<Site> sites = new HashSet<Site>();
    ...

    User user = new User();
    user.setSites(sites);
    userDaoImpl.saveOrUpdate(user); 

    User retrievedUser = userDaoImpl.findByIdFetchSites(1);

    userDaoImpl.getCurrentSession().close();

    assertTrue(retrievedUser.getSites().containsAll(sites));
}

但是,当我尝试这个时,我得到以下异常:

org.springframework.transaction.TransactionSystemException: Could not roll back Hibernate transaction; nested exception is org.hibernate.TransactionException: rollback failed
at org.springframework.orm.hibernate4.HibernateTransactionManager.doRollback

我假设是由于@DirtiesContext(我在每次测试之前需要一个干净的数据库) . 如何在测试期间确保hibernate不会为我的关联进行延迟加载?

1 回答

  • 4

    问题是,在同一事务中,您创建一个包含站点的用户,然后检索此用户 . 发生的事情是persist()调用将用户的站点放在会话缓存中,而查询(无论它做什么)都会返回已经在缓存中的用户 .

    所以你有以下解决方案:

    • 在第一个事务中保存用户及其站点,然后在第二个事务中调用DAO方法并使用 Hibernate.isInitialized() 来测试查询是否加载了用户集合 . 请注意,调用 retrievedUser.getSites().containsAll(sites) 将触发网站的延迟加载,因此不是测试查询急切地获取该集合的正确方法 .

    • 保存用户及其站点,清除会话,然后调用DAO方法,然后使用 Hibernate.isInitialized() 测试查询是否加载了用户集合 .

    • 使测试成为非事务性的,并使DAO事务性,以便DAO返回已分离的实体 . 然后,如果DAO没有急切地获取集合,那么从集合中获取元素将抛出异常 .

相关问题