我有一个如下所示的实体:
@Entity
@Table(uniqueConstraints={@UniqueConstraint(columnNames={"slug"})})
public class BlogPost {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String title;
@Column
private String slug;
}
我想在坚持之前通过执行以下操作生成slug的值:
- 将标题从例如博客文章标题到博客文章标题
- 确保blog-post-title在BlogPost表中是唯一的,如果它不唯一,我想在标题上附加一些后缀,这样它就变成了例如blog-post-title-2
由于我需要在很多实体上执行此操作,因此我最初的想法是创建一个 EntityListener,它可以在 @PrePersist 处执行此操作。但是,文档通常指出我不应该调用 EntityManager 或 Query 方法,也不应该从生命周期回调访问任何其他实体对象。我需要这样做,以确保我生成的 slug 确实是唯一的。
我试图厚颜无耻,但无论如何,使用 Spring 将存储库自动连接到 EntityListener 中确实非常困难。
我应该如何最好地解决这个问题?
谢谢!
最佳答案
OndrejM 和 MirMasej 都绝对正确,生成 slug 不是在实体中完成的事情。我希望 EntityListeners 能够更“聪明”一点,但这不是一个选择。
我最终所做的是使用方面来完成我想要的事情。我不是“ Hook ”实体,而是 Hook CrudRepository 的 save 方法。
首先,我创建了一个注释,这样我就可以识别哪个字段需要被标记:
@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 以及存储库上的这两个方法:countBySlug 和 countBySlugAndIdNot。
再次感谢您的回答。
关于spring - 如何在查询数据库时为 JPA 实体中的列生成值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32233089/