我有一个带有延迟加载的网站集的用户模型:
@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 回答
问题是,在同一事务中,您创建一个包含站点的用户,然后检索此用户 . 发生的事情是persist()调用将用户的站点放在会话缓存中,而查询(无论它做什么)都会返回已经在缓存中的用户 .
所以你有以下解决方案:
在第一个事务中保存用户及其站点,然后在第二个事务中调用DAO方法并使用
Hibernate.isInitialized()
来测试查询是否加载了用户集合 . 请注意,调用retrievedUser.getSites().containsAll(sites)
将触发网站的延迟加载,因此不是测试查询急切地获取该集合的正确方法 .保存用户及其站点,清除会话,然后调用DAO方法,然后使用
Hibernate.isInitialized()
测试查询是否加载了用户集合 .使测试成为非事务性的,并使DAO事务性,以便DAO返回已分离的实体 . 然后,如果DAO没有急切地获取集合,那么从集合中获取元素将抛出异常 .