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