Home > OS >  Hibernate search does not remove old value from lucene index when the object is deleted via an @NoRe
Hibernate search does not remove old value from lucene index when the object is deleted via an @NoRe

Time:07-05

I have a NoRepositoryBean Jpa interface that has one custom jpa method called deleteAllByIdIn(...) which is inherited by some concrete JpaRepositories. For some reason this custom delete method is ignored by Hibernate Search. Whenever an entity is deleted through this custom method its value is not removed from the lucene index after the delete is done. I will explain the problem some more further down this post; but first here's the code

@NoRepositoryBean
public interface NameTranslationDao<T extends NameTranslation> extends JpaRepository<T, Long> {

    @Modifying
    @Transactional
    @Query(value = "DELETE FROM #{#entityName} c WHERE c.id IN :translationsToDelete")
    public void deleteAllByIdIn(@Param("translationsToDelete") Set<Long> translationsToDelete);

}

Heres a JpaRepository subclass that extends this interface:

@Repository
@Transactional(readOnly = true)
public interface LifeStageCommonNameTranslationDao extends CommonNameTranslationDao<LifeStageCommonNameTranslation> {

}

Theres another @NoRepositoryBean interface in-between the concrete JpaRepository and the NameTranslationDao NoRepositoryBean. That one is called CommonNameTranslationDao but it doesn't override the custom method in any way, so it is unlikely the cause of the problem, nevertheless heres the code of that repository:

@NoRepositoryBean
public interface CommonNameTranslationDao<T extends NameTranslation> extends NameTranslationDao<T> {

    @Deprecated
    @Transactional(readOnly = true)
    @Query("SELECT new DTOs.AutoCompleteSuggestion(u.parent.id, u.autoCompleteSuggestion) FROM #{#entityName} u WHERE u.autoCompleteSuggestion LIKE :searchString% AND deleted = false  AND (u.language.id = :preferredLanguage OR u.language.id = :defaultLanguage)")
    List<AutoCompleteSuggestion> findAllBySearchStringAndDeletedIsFalse(@Param("searchString") String searchString, @Param("preferredLanguage") Long preferredLanguage, @Param("defaultLanguage") Long defaultLanguage);

    @Transactional(readOnly = true)
    @Query(nativeQuery = true, value = "SELECT s.translatedName FROM #{#entityName} s WHERE s.language_id = :preferredLanguage AND s.parent_id = :parentId LIMIT 1")
    public String findTranslatedNameByParentAndLanguage(@Param("preferredLanguage") Long languageId, @Param("parentId") Long parentId);

    @Modifying
    @Transactional
    @Query(nativeQuery = true, value = "DELETE FROM #{#entityName} WHERE id = :id")
    void hardDeleteById(@Param("id") Long id);

    @Modifying
    @Transactional
    @Query(nativeQuery = true, value = "UPDATE #{#entityName} c SET c.deleted = TRUE WHERE c.id = :id")
    void softDeleteById(@Param("id") Long id);

}

Also, heres the code of the LifeStageCommonNameTranslation entity class:

    @Entity
    @Indexed
    @Table(
            uniqueConstraints = {
                    @UniqueConstraint(name = "UC_life_cycle_type_language_id_translatedName", columnNames = {"translatedName", "parent_id", "language_id"})
            },
            indexes = {
                    @Index(name = "IDX_lifestage", columnList = "parent_id"),
                    @Index(name = "IDX_translator", columnList = "user_id"),
                    @Index(name = "IDX_species_language", columnList = "language_id, parent_id, deleted"),
                    @Index(name = "IDX_autoCompleteSuggestion_language", columnList = "autoCompleteSuggestion, language_id, deleted")})
    public class LifeStageCommonNameTranslation extends NameTranslation<LifeStage> implements AuthorizationSubject {
    
        @Id @DocumentId
        @GenericGenerator(
                name = "sequenceGeneratorLifeStageCommonNameTranslation",
                strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
                parameters = {
                        @org.hibernate.annotations.Parameter(name = "sequence_name", value = "_lifestagecommonnametranslation_hibernate_sequence"),
                        @org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled"),
                        @org.hibernate.annotations.Parameter(name = "initial_value", value = "1"),
                        @org.hibernate.annotations.Parameter(name = "increment_size", value = "25"),
                        @org.hibernate.annotations.Parameter(name = "prefer_sequence_per_entity", value = "true")
                }
        )
        @GeneratedValue(
                strategy = GenerationType.SEQUENCE,
                generator = "sequenceGeneratorLifeStageCommonNameTranslation"
        )
        @Field(analyze = Analyze.NO, store = Store.YES, name = "parentId")
        private Long id;
    
        @IndexedEmbedded(includeEmbeddedObjectId = true)
        @ManyToOne(fetch = FetchType.LAZY)
        private LifeStage parent;
    
        @Field(index = NO, store = Store.YES)
        private String autoCompleteSuggestion;

//Getters and setters ommitted

The problem is the following: Whenever i use the inherited deleteAllByIdIn() method on LifeStageCommonNameTranslationDao then Hibernate Search will not remove the autoCompleteSuggestion field value from the lucene index after the entity has been deleted. If however i use the standard deleteById() JpaRepository method to delete the entity then the field value is removed from the lucene index.

Both the custom and the standard delete method were called within a @Transactional annotated method and i also called the flush() jpaRepository method right afterwards. I did this because I've read that this can sometimes help to update the lucene index. But in the case of deleteAllByIdIn() calling flush() afterwards did not help at all.

I already ruled out the possiblity that the problem was caused by the spEL expression in the SQL query. I tested this by replacing #{#entityName} with a concrete entity name like LifeStageCommonTranslation and then calling the deleteAllByIdIn() delete method. But the problem still persisted. The lucene index still did not remove the autoSuggestionText field value after the delete.

I can easily solve this problem by simply using the standard jpa method deleteById() but i want to know why the custom made jpa method deleteAllByIdIn() does not cause Hibernate search to update the lucene index.

CodePudding user response:

Hibernate Search detects entity change events happening in your Hibernate ORM Session/EntityManager. This excludes insert/update/delete statements that you wrote yourself in JPQL or native SQL queries.

The limitation is documented here: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#limitations-changes-in-session

The workaround is documented there too:

One workaround is to reindex explicitly after you run JPQL/SQL queries, either using the MassIndexer or manually.

EDIT: And of course your workaround might be valid as well, if deleteById loads the entity in the session before deleting it (I'm not that familiar with the internals of Spring Data JPA):

I can easily solve this problem by simply using the standard jpa method deleteById() but i want to know why the custom made jpa method deleteAllByIdIn() does not cause Hibernate search to update the lucene index.

  • Related