我有一个NoRepositoryBean
Jpa接口,它有一个名为deleteAllByIdIn(...)
的自定义Jpa方法,它由一些具体的Jpa存储库继承。由于某些原因,Hibernate Search
忽略了此自定义删除方法。每当通过此自定义方法删除实体时,在删除完成后,其值不会从lucene索引中删除。我将在这篇文章中进一步解释这个问题;但首先是代码
@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);
}
这里有一个扩展此接口的JpaRepository
子类:
@Repository
@Transactional(readOnly = true)
public interface LifeStageCommonNameTranslationDao extends CommonNameTranslationDao<LifeStageCommonNameTranslation> {
}
在具体的JpaRepository和NameTranslationDao NoRepositoryBean之间还有另一个@NoRepositoryBean
接口。这个方法被称为CommonNameTranslationDao
,但它不会以任何方式覆盖自定义方法,所以它不太可能是问题的原因,不过这里有该存储库的代码:
@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);
}
此外,这里还有LifeStageCommonNameTranslation
实体类的代码:
@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
问题如下:每当我在LifeStageCommonNameTranslationDao
上使用继承的deleteAllByIdIn()
方法时,在删除实体后,Hibernate Search
将不会从lucene index
中删除autoCompleteSuggestion
字段值。但是,如果我使用标准deleteById()
JpaRepository方法删除实体,则字段值将从lucene index
中删除。
自定义和标准删除方法都是在@Transactional
注释的方法中调用的,之后我还调用了flush()
jpaRepository方法。我这样做是因为我读到这有时可以帮助更新lucene指数。但在deleteAllByIdIn()
的情况下,随后呼叫flush()
根本没有帮助。
我已经排除了问题是由SQL查询中的spEL
表达式引起的可能性。我通过用LifeStageCommonTranslation
这样的具体实体名称替换#{#entityName}
,然后调用deleteAllByIdIn()
delete方法来测试这一点。但问题仍然存在。lucene索引在删除后仍然没有删除autoSuggestionText
字段值。
我可以简单地使用标准的jpa方法deleteById()
来解决这个问题,但我想知道为什么定制的jpa方式deleteAllByIdIn()
不会导致Hibernate搜索更新lucene索引。
Hibernate Search检测在Hibernate ORMSession
/EntityManager
中发生的实体更改事件。这不包括您在JPQL或本机SQL查询中自己编写的insert
/update
/delete
语句。
此处记录了限制:https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#limitations-会话中的更改
解决方法也记录在那里:
一种解决方法是在运行JPQL/SQL查询后显式重新索引,可以使用MassIndexer或手动重新索引。
EDIT:当然,如果deleteById
在删除实体之前在会话中加载实体,那么您的变通方法也可能有效(我对Spring Data JPA的内部结构不太熟悉):
我只需使用标准的jpa方法deleteById()就可以很容易地解决这个问题,但我想知道为什么自定义的jpa方式deleteAllByIdIn()不会导致Hibernate搜索更新lucene索引。