2014-11-02 10 views
7

Sto provando ad usare Hibernate Search nel mio progetto (scrivendo test adesso usando junit + dbunit), ma la ricerca di query non restituisce alcun risultato. Ho lavorato su questo ieri e ho concluso che il problema è Hibernate Search non funziona bene con dbunit @DatabaseSetup (problema simile a questa domanda senza risposta: link). Andrò con maggiori dettagli, ma abeti andiamo con ordine, c'è la mia classe entità:Hibernate Search non indice/reindex entità

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

risparmio che al db dal mio DAO:

@Repository 
public class UserDAOImpl implements UserDAO { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public long save(User toSave) { 

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

Si tratta di codice responsabile per l'esecuzione di query di 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; 
    } 

Ed è così che cosa è configurato in primavera 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> 

Ora come ho provato in un primo momento. Ho configurato il mio set di dati di test con DBUnit e metodo di prova creato in questo modo:

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

Dopo aver esaminato la documentazione Hibernate Search ho trovato fullTextSession.createIndexer().startAndWait(); metodo. L'ho usato ma non funziona ancora per le righe da @DatabaseSetup. In ogni caso ha funzionato con le righe che ho putted prima della prova "a mano" con SQL così ho pensato che è unico problema con DBUnit e appena scritto installazione con @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; 
     } 

    } 

ed eseguire questo test:

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

E non funziona anche se i dati vengono salvati dalla sospensione. Ho cercato di trovare un bug e ho ripristinato il mio codice nella versione di eariel in cui il test stava passando (quello con @DatabaseSetup ma solo per le righe salvate con il mio dao) e ora anche questo non viene passato. Sono abbastanza confuso e fuori dalle idee perché non indicizza nuovi oggetti, per non dire perché non reindirizzi tutto il database quando viene chiamato un indicizzatore di massa. Qualsiasi aiuto sarà apprezzato.

EDIT:

Dopo potenziali risposte Ho fatto alcuni altri esami. Per quanto riguarda il fatto che la ricerca a volte è risultata con righe duplicate o triplicate, ho provato .purgeAll() e ho modificato il provider di indicizzazione in RAM per assicurarmi che i miei indici siano puliti all'avvio del test. Non è cambiato di base in alcun modo. Per costruire il mio indice ho usato .startAndWait() come menzionato prima. Ho provato a costruirlo "a mano" con .index() ma ho avuto alcuni problemi di transazioni annidate quando ho provato a usare fullTextSession. L'operazione di commit esplicita (o l'impostazione di @Rollback(false) - provato entrambi) non funziona. Tutto ciò che ho provato ho trovato nella documentazione di ricerca di Hibernate - link. L'indicizzazione e la ricerca funzionano bene se salvo qualcosa con DAO prima di cercarlo, ma facendo lo stesso @Prima e poi la ricerca non funziona.

risposta

3

Quando mi ricordo bene, Hibernate Search aggiornerà l'indice quando invii una transazione.

Questo non è un problema per il codice normale, ma nei test questo comportamento può causare un problema, perché un modello comune per i test è che si avvia una transazione quando si avvia il test e alla fine del test si svolge il ruolo la transazione torna, ma non li invii mai.

Per verificare che questa sia la causa del problema, creare un test che avvia una nuova transazione esplicita, modifica qualcosa e quindi esegue il commit della transazione. Quindi dopo il commit controlla il tuo indice di ricerca hiberante.

+0

Purtroppo dichiarare esplicitamente la transazione non aiuta. Cosa c'è di più - i risultati di ricerca a volte non restituiscono nulla, quindi alla prossima esecuzione di test il risultato viene raddoppiato o triplicato (ad esempio per la ricerca "[email protected]" lo trova e restituisce tre oggetti anche quando c'è una sola riga in db). Trova anche risultati strani, come per la ricerca di "[email protected]" restituisce oggetti che non hanno "[email protected]" nel campo email. Non riesco a vedere alcun motivo in questi errori, quindi non posso nemmeno dire quando si verifica. Inoltre sembra reindicizzare più entità che dovrebbe quando viene chiamato startAndWait. – Plebejusz

1

Come indicato in questo Hibernate Search doesn't index/reindex entities, è necessario eseguire il commit esplicito della transazione dopo aver salvato i dati per l'indicizzazione. L'indicizzazione avviene su una sincronizzazione post transazione (almeno per impostazione predefinita).

È possibile provare a utilizzare l'API di indicizzazione manuale o l'indicizzatore di massa. Non sono sicuro del motivo per cui questo non ha funzionato per te. Inoltre non sono sicuro di come esattamente @DatabaseSetup funzioni e si agganci al ciclo di vita di JUnit.

Per quanto riguarda i risultati tripli. È possibile che si stia utilizzando un indice basato su file system (utilizzato per impostazione predefinita) che crea un indice Lucene basato su file che non viene ripulito tra le esecuzioni di test. Utilizzare un indice RAM o assicurarsi che l'indice basato su file venga ripulito.

Potrebbe essere d'aiuto, se condividi la configurazione delle proprietà di Hibernate.

+0

Puoi vedere la mia configurazione delle proprietà di Hibernate nel mio post originale (4 snippet di codice) e anche lì puoi vedere che sto effettivamente usando l'indice basato sul file system. Ho bisogno che sia basato su file, ma a scopo di test l'ho cambiato in ram e non ha funzionato. Per maggiori dettagli guarda la modifica alla mia domanda. – Plebejusz

Problemi correlati