28 Eylül 2021 Salı

HibernateSearch MassIndexer Arayüzü

Giriş
Şu satırı dahil ederiz
import org.hibernate.search.MassIndexer;
massIndexer metodu - Class
Hangi sınıfların indeksleneceğini belirtmek için kullanılır

Örnek
Şöyle yaparız
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.massindexing.MassIndexer;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;

@Transactional
@Component
public class Indexer {

  private EntityManager entityManager;

  private static final int THREAD_NUMBER = 4;

  public Indexer(EntityManager entityManager) {
    this.entityManager = entityManager;
  }

  public void indexPersistedData(String indexClassName) throws IndexException {

    try {
      SearchSession searchSession = Search.session(entityManager);

      Class<?> classToIndex = Class.forName(indexClassName);
      MassIndexer indexer = searchSession
        .massIndexer(classToIndex)
        .threadsToLoadObjects(THREAD_NUMBER);

        indexer.startAndWait();
    } catch (Exception e) {
      ...
  }
}
Şöyle yaparız
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ApplicationRunner buildIndex(Indexer indexer) throws Exception {
        return (ApplicationArguments args) -> {
            indexer.indexPersistedData("com.mozen.springboothibernatesearch.model.Plant");
        };
    }
}
startAndWait metodu
Burada massIndexer() metoduna sınıf ismi geçilmiyor.

Örnek
Şöyle yaparız
@Configuration
public class HibernateSearchIndexBuild implements ApplicationListener<ApplicationReadyEvent>{

  @Autowired
  private EntityManager entityManager;

  @Override
  @Transactional
  public void onApplicationEvent(ApplicationReadyEvent event) {
    SearchSession searchSession = Search.session(entityManager);
    MassIndexer indexer = searchSession.massIndexer()
.idFetchSize(150)
.batchSizeToLoadObjects(25).threadsToLoadObjects(12);
    try {
      indexer.startAndWait();
    } catch (InterruptedException e) {
      ...
    }
  }
}

23 Eylül 2021 Perşembe

HibernateSearch Kullanımı

Maven
Açıklaması şöyle
Hibernate Search provides you with both Lucene and ElasticSearch implementations that are highly optimized for full-text search.
Örnek - Lucene
Şöyle yaparız
<dependency>
<groupId>org.Hibernate.search</groupId> <artifactId>Hibernate-search-mapper-orm</artifactId> <version>6.0.2.Final</version> </dependency> <dependency> <groupId>org.Hibernate.search</groupId> <artifactId>Hibernate-search-backend-lucene</artifactId> <version>6.0.2.Final</version> </dependency>
SpringBoot İle Kullanım
Spring için şu kütüphaneyi dahil ederiz
spring-boot-hibernate-search
Index dizinini belirtmek için şöyle yaparız
# HIBERNATE SEARCH
spring.jpa.properties.hibernate.search.backend.directory.root=/home/indexes/
yaml ile şöyle yaparız
server:
    port: 9000

spring:
    datasource:
        url: jdbc:h2:mem:mydb
        username: mozen
        password: password
    jpa:
        open-in-view: false
        properties:
            hibernate:
                search:
                    backend:
                        type: lucene
                        directory.root: ./data/index
Data Model
Entity üzerinde
@Indexed - Sınıfın indeksleneceğini belirtir
@FullTextField - Metin alanlarda arama içindir. Diğer tipte alanlar için başka anotasyonlar var
anotasyonları kullanılır

MassIndexer ile indeksleme başlatılır
SearchSession ile arama yapılır

Repository
Spring için şöyle bir kod ekleriz. Burada @NoRepositoryBean kullanılıyor, böylece bu sınıf repository olarak algılanmaz.
@NoRepositoryBean
public interface SearchRepository<T, ID extends Serializable> 
  extends JpaRepository<T, ID> {

  List<T> searchBy(String text, int limit, String... fields);
}
searchBy metodunun gerçekleştirimini ekleriz.  Spring kuralları gereği interface ismine Impl son ekini vererek yeni bir sınıf yaratırız. Burada SearchSession hibernate sınıfı. Burada fetch() metoduna limit değeri geçiliyor.  
import org.hibernate.search.mapper.orm.session.SearchSession;

@Transactional
public class SearchRepositoryImpl<T, ID extends Serializable> 
  extends SimpleJpaRepository<T, ID>
        implements SearchRepository<T, ID> {

  private final EntityManager entityManager;

  public SearchRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
    super(domainClass, entityManager);
    this.entityManager = entityManager;
  }

  public SearchRepositoryImpl(
          JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
  }

  @Override
  public List<T> searchBy(String text, int limit, String... fields) {

    SearchResult<T> result = getSearchResult(text, limit, fields);

     return result.hits();
  }

  private SearchResult<T> getSearchResult(String text, int limit, String[] fields) {
    SearchSession searchSession = Search.session(entityManager);

    SearchResult<T> result = searchSession
      .search(getDomainClass())
      .where(f -> f.match().fields(fields).matching(text).fuzzy(2))
      .fetch(limit);
    return result;
  }
}
Artık yeni bir repository şöyledir
@Repository
public interface PlantRepository extends SearchRepository<Plant, Long> {
}
Örnek - Sayfalama
Şöyle yaparız. Burada fetch() metoduna offset + limit değerleri geçiliyor. 
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageDTO<T> {

    private List<T> content;
    private long total;
}

@Override
public PageDTO<T> searchPageBy(String text, int limit, int offset, String... fields) {
  SearchResult<T> result = getSearchResult(text, limit, offset, fields);

  return new PageDTO<T>(result.hits(), result.total().hitCount());
}

private SearchResult<T> getSearchResult(String text, int limit, int offset, 
  String[] fields) {
  SearchSession searchSession = Search.session(entityManager);

  SearchResult<T> result = searchSession
    .search(getDomainClass())
    .where(f -> f.match().fields(fields).matching(text).fuzzy(2))
    .fetch(offset, limit);
    return result;
}



1 Eylül 2021 Çarşamba

SequenceStyleGenerator Sınıfı

Örnek
Şöyle yaparız
@Entity
public class Account {
  @Id
  @GenericGenerator(
    name = "account_id_generator",
    strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
    parameters = {
      @Parameter(name = "sequence_name", value = "account_id_seq"),
      @Parameter(name = "increment_size", value = "50"),
      @Parameter(name = "optimizer", value = "pooled-lo")
    })
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "account_id_generator")
    private Long id;
    ...
}