3

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

@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 事务注释(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 在测试期间不会为我的关联进行延迟加载?

4

1 回答 1

8

问题是,在同一个事务中,您创建了一个带有站点的用户,然后检索该用户。发生的情况是,persist() 调用将用户及其站点放入会话缓存中,而无论它做什么,查询都会返回已经在缓存中的用户。

所以你有以下解决方案:

  • 将用户及其站点保存在第一个事务中,然后在第二个事务中调用您的 DAO 方法并用于Hibernate.isInitialized()测试查询是否加载了用户集合。请注意,调用retrievedUser.getSites().containsAll(sites)将触发站点的延迟加载,因此不是测试查询是否急切获取集合的正确方法。
  • 保存用户及其站点,清除会话,然后调用您的 DAO 方法,然后用于Hibernate.isInitialized()测试用户集合是否由查询加载。
  • 使测试成为非事务性的,并使 DAO 具有事务性,以便 DAO 返回分离的实体。如果 DAO 没有急切地获取集合,那么从集合中获取元素将引发异常。
于 2013-09-27T18:50:30.510 回答