2013-09-27 91 views
0

我有一個懶洋洋地加載網站收集用戶模式:JUnit的休眠:測試延遲關聯獲取

@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是春事務註釋(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在測試期間不會爲我的關聯執行延遲加載?

回答

3

問題是,在同一個事務中,您使用網站創建用戶,然後檢索此用戶。會發生什麼是persist()調用將用戶的網站置於會話緩存中,並且查詢(無論它執行什麼操作)會返回已存在於緩存中的用戶。

所以,你有以下解決方案:

  • 保存用戶及其在第一個交易網站,然後撥打第二個事務的DAO方法和使用Hibernate.isInitialized()測試用戶的集合被加載查詢。請注意,調用retrievedUser.getSites().containsAll(sites)將觸發站點的延遲加載,因此不是正確的方式來測試集合是否被查詢熱切地提取。
  • 保存用戶及其網站,清除會話,然後調用您的DAO方法,然後使用Hibernate.isInitialized()來測試查詢加載的用戶集合。
  • 使測試非事務性和DAO事務性,以便DAO返回分離的實體。然後,如果DAO尚未熱切地獲取集合,那麼獲取集合中的元素將會引發異常。
+0

似乎我在課堂上需要'@Transactional',或者它抱怨沒有課程...所以我應該在課堂上有'@Transactional(propagation = Propagation.REQUIRES_NEW)',然後3個方法?即:'@Test public void myTest(){insertUserWithSites(); assertSitesRetrievedWithUser(); }','@Transactional public void insertUserWithSites(){...}'和'public void assertSitesRetrievedWithUser(){...}'? – JimJay

+0

不是。所有方法仍將共享相同的事務。如果你想保持你的方法事務性,那麼使用我的答案中解釋的第二種方法。 –