2014-11-02 64 views
7

我想在我的項目中使用Hibernate搜索(現在使用junit + dbunit編寫測試),但搜索查詢不會返回任何結果。我昨天就這樣做了,得出的結論是,Hibernate Search與dbunit @DatabaseSetup不兼容(類似於此未解答的問題:link)。我會用更多的細節去,但杉杉的事情首先,有我的實體類:Hibernate搜索不索引/ reindex實體

@Entity 
@Indexed 
public class User { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "userId") 
    private Long id; 
    (...) 
    @Column(nullable = false, unique = true) 
    @Field(index = Index.YES, analyze=Analyze.YES, store=Store.NO) 
    private String email; 
    (...) 
    @Column(nullable = false, unique = true) 
    @Field(index = Index.YES, analyze=Analyze.YES, store=Store.NO) 
    private String username; 
    (...) 
} 

我將它保存在我的DAO DB:

@Repository 
public class UserDAOImpl implements UserDAO { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public long save(User toSave) { 

     return (Long) this.sessionFactory.getCurrentSession().save(toSave); 
    } 
(...) 
} 

這是負責運行Lucene的查詢代碼:

@Override 
    public List<User> searchByEmail(String email) throws InterruptedException { 

     return generateHibernateSearchQueryFor("email", email).list(); 
    } 

    private org.hibernate.Query generateHibernateSearchQueryFor(String field, String searchParam) { 

     FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession()); 
     QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(User.class).get(); 

     org.apache.lucene.search.Query lQuery = queryBuilder.keyword().onFields(field).matching(searchParam).createQuery(); 
     org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(lQuery, User.class); 

     return fullTextQuery; 
    } 

而這件事是如何在Spring配置配置:

<bean id="hibernate4AnnotatedSessionFactory" 
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
     <property name="dataSource" ref="dataSource" /> 
     <property name="packagesToScan" value="me.ksiazka.model" /> 
     <property name="hibernateProperties"> 
      <props> 
       <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> 
       <prop key="hibernate.show_sql">false</prop> 
       <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop> 
       <prop key="hibernate.cache.use_second_level_cache">true</prop> 
       <prop key="hibernate.cache.use_query_cache">true</prop> 
       <prop key="hibernate.hbm2ddl.auto">create</prop> 

       <prop key="hibernate.search.default.directory_provider">filesystem</prop> 
       <prop key="hibernate.search.default.indexBase">src/searching_indexes</prop> 
      </props> 
     </property> 
    </bean> 

現在我是怎麼開始測試的。我配置我用DbUnit和創建測試方法測試數據集是這樣的:

@Test 
    @DatabaseSetup("classpath:/testsDataset.xml") 
    public void searchByEmailTest() { 

     User u1 = new User("Maciej", "Adamowicz", "k2", "[email protected]", "MacAda"); 
     userDAO.save(u1); 

     List<User> u = null; 
     try { 
      //It worked at first - as new user was saved with hibernate he got his index in hibernate search indexes folder and searching found him. 
      u = searchService.searchByEmail("[email protected]"); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     //I know there should be asserts, its just for simplification for need of moment. 
     System.out.println(":: " + u.size()); 
     System.out.println(":: " + u.get(0).getName()); 
    } 


    List<User> u2 = null; 
    try { 
     //[email protected] is in db - setted up by @DatabaseSetup 
     u2 = searchService.searchByEmail("[email protected]"); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    //This didnt work, rows putted into db by dbunit doesn't have indexes in my indexing folder. 
    System.out.println(":: " + u2.size()); 
    System.out.println(":: " + u2.get(0).getName()); 
} 

尋找Hibernate Search的資料後,我發現fullTextSession.createIndexer().startAndWait(); 方法。我用它,但它仍然不適用於來自@DatabaseSetup的行。反正它用,所以我認爲它是用DbUnit唯一的問題是我考試前的推杆「手」與SQL行工作,只是寫了@Before設置:

@Before 
    public void setupDatabase() { 

     if(!doneBefore) { 

      try { 
       //It calls createIndexer().startAndWait() to make sure everything is indexed before test 
       searchService.reindex(); 
      } catch (InterruptedException e) {  
       e.printStackTrace(); 
      } 

      User u1 = new User("Maciej", "Adamowicz", "k2", "[email protected]", "MacAda"); 
      userDAO.save(u1); 

      doneBefore = true; 
     } 

    } 

並運行這個測試:

@Test 
    public void searchByEmailTest() { 

     List<User> u = null; 
     try { 
      u = searchService.searchByEmail("[email protected]"); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     //Also asserts here, I know. 
     System.out.println(":: " + u.size()); 
     System.out.println(":: " + u.get(0).getName()); 
    } 

雖然通過休眠保存數據,但它不起作用。我試圖找到bug並將我的代碼恢復到了測試通過的版本(使用@DatabaseSetup的版本,但僅限於使用我的dao保存的行),現在這個版本也不能通過。我非常困惑,爲什麼不索引新對象,並不是說爲什麼在調用大量索引器時不重新索引所有數據庫。任何幫助將不勝感激。

編輯:

潛在的答案後,我做了一些更多的測試。關於事實上搜索有時會導致兩倍或三倍的行,我嘗試了.purgeAll()並將索引提供程序更改爲RAM,以確保在開始測試時我的索引是乾淨的。它基本上沒有改變。如前所述,我使用.startAndWait()來構建我的索引。嘗試用.index()「手動」構建它,但是在嘗試使用fullTextSession時遇到了一些嵌套事務問題。明確提交交易(或設置@Rollback(false) - 都嘗試)不起作用。 我在Hibernate搜索文檔中發現的所有內容 - link。 索引和搜索工作正常,如果我在搜索之前使用DAO保存某些內容,但是執行相同的操作時,@Before然後搜索不起作用。

回答

3

當我記得沒錯的時候,當你提交一個事務時,Hibernate Search會更新索引。

這對於普通代碼來說沒有問題,但是在測試中,這種行爲可能會導致問題,因爲測試的常見模式是,當您開始測試時啓動事務,並且在測試結束時您扮演角色交易回來了,但你永遠不會提交。

要驗證這是您問題的原因,請創建一個測試,以啓動明確的新事務,修改某些事項並提交事務。然後在提交後檢查你的hiberante搜索索引。

+0

不幸的是,顯式聲明事務並沒有幫助。還有什麼 - 搜索結果有時不會返回任何內容,然後在下一次測試運行時返回結果的兩倍或三倍(例如,搜索「[email protected]」時發現它並返回三個對象,即使在db中只有一行時也是如此)。它也會發現奇怪的結果,比如搜索「[email protected]」它會返回在其電子郵件字段中沒有「[email protected]」的對象。我在這個錯誤中看不到任何模式,所以我什至不知道它們中的哪一個發生。 另外,它似乎重新索引更多的實體,它應該當startAndWait被調用。 – Plebejusz 2014-11-03 14:40:52

1

正如在Hibernate Search doesn't index/reindex entities中所提到的,您需要在保存數據以便索引發生之後顯式提交您的事務。索引發生在事務後同步(至少默認情況下)。

您可以嘗試使用手動索引API或質量索引器。我不知道爲什麼這不適合你。我也不確定@DatabaseSetup究竟是如何工作和掛鉤到JUnit生命週期的。

關於三重結果。您可能正在使用基於文件系統的索引(默認情況下使用),該索引創建基於文件的Lucene索引,該索引在測試運行之間未被清​​理。使用RAM索引或確保基於文件的索引得到清理。

如果你分享你的Hibernate屬性配置,它可能會有所幫助。

+0

你可以在我的原始文章(第四個代碼片段)中看到我的Hibernate屬性配置,在那裏你可以看到我的確在使用基於文件系統的索引。我需要它是基於文件的,但爲了測試的目的,我將它改爲ram,但它不起作用。有關更多詳細信息,請查看編輯我的問題。 – Plebejusz 2014-11-04 16:10:43