2014-11-02 15 views
7

Próbuję użyć Hibernate Search w moim projekcie (pisanie testów teraz za pomocą junit + dbunit), ale zapytanie wyszukiwania nie zwraca żadnych wyników. Pracowałem nad tym wczoraj i doszedłem do wniosku, że problem jest Hibernate Search nie działa dobrze z dbunit @DatabaseSetup (podobny problem jak w tym nieodebranym pytaniu: link). Pójdę z większą ilością szczegółów, ale jodły rzeczy pierwsze, nie jest moja klasa podmiot:Hibernate Search nie indeksuje/reindeksuje obiektów

@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; 
    (...) 
} 

zapisać go do db mojego DAO:

@Repository 
public class UserDAOImpl implements UserDAO { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public long save(User toSave) { 

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

Jest to kod odpowiedzialny za prowadzenie kwerendy 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; 
    } 

I tak sprawa jest skonfigurowany w wiosennej config:

<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> 

Teraz jak się najpierw testowałem. I skonfigurowany zestaw danych z mojego testowania dbunit i stworzył metodę badania takiego:

@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()); 
} 

Po patrząc hibernacji dokumentację Search znalazłem fullTextSession.createIndexer().startAndWait(); metody. Użyłem go, ale nadal nie działa dla wierszy z @DatabaseSetup. W każdym razie to działało z wierszy, które ja putted przed testem „ręcznie” z sql więc myślałem, że to jest tylko problem z dbunit i po prostu napisał konfigurację z @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; 
     } 

    } 

i uruchomieniu tego testu:

@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()); 
    } 

I to nie działa, mimo że dane są zapisywane przez hibernację. Próbowałem znaleźć błąd i przywróciłem mój kod do wersji eariel, w której test był przekazywany (ten z @DatabaseSetup, ale tylko dla wierszy zapisanych z moim dao) i teraz ten też nie przechodzi. Jestem bardzo zdezorientowany i nie ma pomysłu, dlaczego nie indeksuje nowych obiektów, nie mówiąc, dlaczego nie reindeksuje całej bazy danych, gdy wywoływany jest masywny indexer. Każda pomoc zostanie doceniona.

EDYTOWANIE:

Po potencjalnych odpowiedziach wykonałem jeszcze kilka testów. W związku z faktem, że wyszukiwanie czasami skutkowało podwójnymi lub potrójnymi wierszami, wypróbowałem .purgeAll() i zmieniłem dostawcę indeksowania na RAM, aby mieć pewność, że moje indeksy są czyste podczas rozpoczynania testowania. Nie zmieniło się to w zasadzie nic. Aby zbudować mój indeks, użyłem .startAndWait(), jak wspomniano wcześniej. Próbowałem budować go "ręcznie" z .index(), ale mam problemy z zagnieżdżonymi transakcjami, gdy próbowałem użyć fullTextSession. Jawnie zatwierdzanie transakcji (lub ustawienie @Rollback(false) - próbował obu) również nie działa. Wszystko, co próbowałem znalazłem w dokumentacji wyszukiwania w Hibernate - link. Indeksowanie i wyszukiwanie działają dobrze, jeśli zapisuję coś z DAO tuż przed jego wyszukaniem, ale robiąc to samo, @Before, a następnie wyszukiwanie nie działa.

Odpowiedz

3

Gdy dobrze pamiętam, to wyszukiwanie w trybie hibernacji zaktualizuje indeks po przesłaniu transakcji.

Nie stanowi to problemu dla normalnego kodu, ale w testach to zachowanie może powodować problem, ponieważ wspólny wzorzec dla testów polega na tym, że po rozpoczęciu testu rozpoczyna się transakcja, a po zakończeniu testu rola transakcja z powrotem, ale nigdy ich nie przesyłasz.

Aby sprawdzić, czy to jest przyczyną problemu, należy utworzyć test, który rozpocznie nową, wyraźną transakcję, zmodyfikować coś, a następnie zatwierdzić transakcję. Następnie po zatwierdzeniu sprawdź swój indeks wyszukiwania hiberante.

+0

Niestety jawnie deklarowanie transakcji nie pomaga. Co więcej - wyniki wyszukiwania czasami nic nie zwracają, a następnie przy następnym uruchomieniu testu zwracają wynik podwójny lub potrójny (np. Dla wyszukiwania "[email protected]" znajduje on i zwraca trzy obiekty, nawet jeśli w db jest tylko jeden wiersz). Znajduje również dziwne wyniki, np. Wyszukując "[email protected]" zwraca obiekty, które nie mają "[email protected]" w polu adresu e-mail. Nie widzę żadnego wzorca w tych błędach, więc nie mogę nawet stwierdzić, kiedy ma miejsce. Również wygląda na to, że reindeksuje więcej jednostek, niż powinno, gdy zostanie wywołana funkcja startAndWait. – Plebejusz

1

Jak wspomniano w tej Hibernate Search doesn't index/reindex entities, należy jawnie zatwierdzić transakcję po zapisaniu danych do indeksowania. Indeksowanie następuje po synchronizacji transakcji (przynajmniej domyślnie).

Możesz spróbować użyć ręcznego interfejsu API do indeksowania lub indeksu masy. Nie wiem, dlaczego to nie zadziałało. Nie jestem również pewien, jak dokładnie działa @DatabaseSetup i przechwytuje do cyklu życia JUnit.

Odnośnie potrójnych wyników. Być może korzystasz z indeksu opartego na systemie plików (używanego domyślnie), który tworzy oparty na pliku indeks Lucene, który nie jest czyszczony między testami. Użyj indeksu RAM lub upewnij się, że indeks oparty na pliku zostanie wyczyszczony.

Może to pomóc, jeśli udostępnisz konfigurację właściwości Hibernuj.

+0

Możesz zobaczyć moją konfigurację właściwości Hibernuj w moim oryginalnym wpisie (4 fragment kodu), a także tam widzę, że rzeczywiście używam indeksu opartego na systemie plików. Potrzebuję go, aby był oparty na plikach, ale dla celów testów zmieniłem go na RAM i nie zadziałało. Aby uzyskać więcej informacji, proszę spojrzeć na edycję na moje pytanie. – Plebejusz