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.