OndrejM 和 MirMasej 都绝对正确,生成 slug 不会在实体中完成。我希望 EntityListeners 可以“更聪明”一点,但这不是一个选择。
我最终做的是使用 aspects 来完成我想要的。我不是“挂钩”实体,而是挂钩 CrudRepository 的保存方法。
首先,我创建了一个注解,这样我就可以识别出哪个字段需要被 sluggified:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Slug {
/**
* The string slug is generated from
*/
String source() default "title";
/**
* Strategy for generating a slug
*/
Class strategy() default DefaultSlugGenerationStrategy.class;
}
然后,我创建了一个类似这样的方面:
@Aspect
@Component
public class SlugAspect {
... // Removed some code for bravity
@Before("execution(* org.springframework.data.repository.CrudRepository+.save(*))")
public void onRepoSave(JoinPoint joinPoint) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object entity = joinPoint.getArgs()[0];
for (Field field: entity.getClass().getDeclaredFields()) {
Slug annotation = field.getAnnotation(Slug.class);
if (annotation != null) {
CrudRepository repository = (CrudRepository) joinPoint.getTarget();
Long count = 0L;
SlugGenerationStrategy generator = (SlugGenerationStrategy)annotation.strategy().newInstance();
String slug = generator.generateSlug(slugOrigin(entity));
if (id(entity) != null) {
Method method = repository.getClass().getMethod("countBySlugAndIdNot", String.class, Long.class);
count = (Long)method.invoke(repository, slug, id(entity));
} else {
Method method = repository.getClass().getMethod("countBySlug", String.class);
count = (Long)method.invoke(repository, slug);
}
// If count is zero, use the generated slug, or generate an incremented slug if count > 0 and then set it like so:
setSlug(entity, slug);
}
}
}
}
如果有人对以下内容感兴趣,我会将代码放在 github 上(尽管它仍然只是一个概念证明):https://github.com/cabrilo/jpa-slug
它依赖于来自 Spring Data 的 CrudRepository 并在 repo 上拥有这两个方法:countBySlug 和 countBySlugAndIdNot。
再次感谢您的回答。