spring - 如何在查询数据库时为 JPA 实体中的列生成值?

标签 spring hibernate jpa

我有一个如下所示的实体:

@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/

相关文章:

java - Spring Data JPA 中的命名实体图 JOINS 结果(需要不同的选项)

java - Spring Boot 基于目录的数据库

java - 关于Spring框架中的AnnotationConfigApplicationContext

java - Hibernate 级联与逆向关系

java - 如何检查数据库中枚举的无效数据

java - 什么是 HibernateTemplate 类?

jakarta-ee - Java EE : Eclipselink transaction missing

java - 为什么方法 contextInitialized 不调用?

java - 如何通过maven导入多个Spring应用程序,其中每个依赖项位于自己的类路径上(以避免依赖项冲突)?

java - 用注释注释的方法的方面,该方法用另一个注释注释