【问题标题】:Spring Data - get entity name/type from RepositorySpring Data - 从存储库获取实体名称/类型
【发布时间】:2018-05-25 08:49:19
【问题描述】:

我正在寻找一种从实现 Spring Data JPA Repository 接口的实例中获取实体类型或类名的方法。

我有许多接口扩展了一个基本接口,这些接口扩展了Repository 接口并定义了一些基本查询。

@NoRepositoryBean
public interface EnumerationRepository<T extends IDatabaseEnumeration> extends Repository<T, String> {
    // ...
}

public interface SampleEnumerationRepository extends EnumerationRepository<SampleEnumeration> {

}

Spring 允许我将所有这些接口的实现作为一个集合注入到一个 bean 中

@Autowired
private Collection<EnumerationRepository<? extends IDatabaseEnumeration>> repositories;

我想将所有这些实现放入Map 中,以便在泛型方法中轻松访问。我想使用实体类型或名称作为键,但我不知道从哪里得到它。有没有办法获得这些属性之一?最好是类类型。

我能够通过这样的查询实现所需的行为。但如果可能,我想避免这种解决方案,因为它会创建对底层数据库的依赖。

@Query(value = "select '#{#entityName}' from dual", nativeQuery = true)
public String getEntityName();

【问题讨论】:

  • 尝试删除“from dual”并删除 nativeQuery 部分。它应该像普通 JPA 查询一样工作。
  • 不幸的是,这并没有奏效。现在我收到此异常,因为查询不完整。 Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected end of subtree [select 'SampleEnumeration']
  • 我会 100% 发誓我在个人项目中做过同样的事情,但我现在找不到。今晚晚些时候我会搜索更多。与此同时,我暂时将“from dual”和 native 保留。

标签: java spring spring-data


【解决方案1】:

@jens-schauder 的回答在我的情况下不起作用,但它为我指明了正确的方向。

Spring 为我注入了扩展 spring Repository 接口的接口实现。因此我必须得到所有接口,过滤掉弹簧内部的接口,所以我最终得到了我定义的那个。然而,这个还不是通用的,所以我必须获得具有通用类型的它的超级接口。

我并不关心性能,因为这个方法只在 Spring 容器初始化期间被调用。

幸运的是,多态在这种情况下工作得很好。所以我只需要在超级接口上实现默认方法。像这样

@NoRepositoryBean
public interface EnumerationRepository<T extends IDatabaseEnumeration> extends Repository<T, String> {

    // ...

    default Class<T> getEntityClass() {
        Type[] interfaces = getClass().getInterfaces();

        for (Type t : interfaces) {
            if (t instanceof Class<?>) {
                Class<?> clazz = (Class<?>) t;

                if (clazz.getPackage().getName().startsWith(ApplicationConst.BASE_PACKAGE)) {

                    // Repositories should implement only ONE interface from application packages

                    Type genericInterface = clazz.getGenericInterfaces()[0];

                    return (Class<T>) ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
                }
            }
        }

        return null;
    }

}

我希望这可能对面临类似问题的其他用户有用。

【讨论】:

  • 我有同样的用例,这确实非常有用。谢谢!
  • 对我来说,执行这段代码大约需要 400 毫秒。不是最快的解决方案,但它可以完成工作。
【解决方案2】:

如果类型参数绑定到接口中的类型,则可以使用以下答案获取类型参数:https://stackoverflow.com/a/1901275/66686

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)yourRepo.getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

【讨论】:

    【解决方案3】:

    试试这个

    import com.fasterxml.classmate.ResolvedType;
    import com.fasterxml.classmate.TypeResolver;
    import org.springframework.aop.support.AopUtils;
    import org.springframework.data.repository.Repository;
    import org.springframework.util.Assert;
    
    import java.util.List;
    
    public class Repospector {
        private static final TypeResolver RESOLVER = new TypeResolver();
    
        public Class<?> getEntityClass(Repository<?, ?> repository) {
            Class<?> targetClass = AopUtils.getTargetClass(repository);
            List<ResolvedType> types = RESOLVER.resolve(targetClass).typeParametersFor(Repository.class);
            Assert.state(types.size() == 2, "Call the whambulance!");
            return types.get(0).getErasedType();
        }
    }
    

    【讨论】:

    • @Andrey 它对你有什么影响?
    【解决方案4】:

    我通过使用 typetools https://github.com/jhalterman/typetools 解决了这个问题

    这是我的代码:

    Class<?>[] classes = TypeResolver.resolveRawArguments(JpaRepository.class, jpaRepositoryInstance.getClass());
    Class<?> entityClz = classes[0];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-20
      • 1970-01-01
      • 1970-01-01
      • 2020-04-12
      • 2021-10-31
      • 1970-01-01
      • 2017-12-02
      • 1970-01-01
      相关资源
      最近更新 更多