Spring Data - 从存储库获取实体名称/类型

问题描述 投票:0回答:5

我正在寻找一种从实现 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();
java spring spring-data
5个回答
5
投票

@jens-schauder 的回答在我的案例中不起作用,但它向我展示了正确的方向。

Spring 向我注入了扩展 spring

Repository
接口的接口实现。因此我必须获取所有接口,过滤掉 spring 内部的接口,所以我最终得到了我定义的接口。然而,这个还不是通用的,所以我必须得到 its 具有通用类型的超级接口。

我真的不关心性能,因为这个方法只在 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;
    }

}

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


1
投票

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

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

0
投票

试试这个

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();
    }
}

0
投票

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

这是我的代码:

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

0
投票

我会根据我最近的经验,对Spring Boot中的一些类或接口进行反思,提出自己的看法

对不起我的英语......我来自巴西。

由于我还不能评论答案🤗,我会写在这个答案中分享我对这个问题的经验

这个答案是关于这个堆栈及其版本的:

JDK 11、带有 Spring MVC、Spring Data JPA 和其他库的 Spring Boot 2.7.11。发表于 2023-05-11.

正如 @ekcrisp 在上面的回答中所说的

您可以使用 Spring 的 GenericTypeResolver 从您的存储库中获取实体类。

我的经验:不幸的是,常见的反射方式无法在 Spring Boot 的存储库中获取

<T>
实体类。我尝试了很多方法。但是这个
GenericTypeResolver
实际上可以从
<T>
获得泛型
JpaRepository<T, ID>
类型。

换句话说,下面的函数可以提供一个类的所有泛型类型作为一个数组。然后你可以选择其中之一:

public Class<?>[] getGenericType( Class<?> classInstance, Class<?> classToGetGenerics ) {
    return GenericTypeResolver.resolveTypeArguments( classInstance, classToGetGenerics );
}

如果您想在“ObjectUtil”本地解决方案中添加解决方案,也许这些方法可用于在任何位置获取泛型类型,例如在

<T>
中获取
<ID>
JpaRepository<T, ID>

@UtilityClass
public class ObjectUtil {
    // ...

    /**
     *
     * @param classInstance
     * @param classToGetGenerics
     *
     * @return the generic classes of the given param, based on class to get generics param.
     *
     * @see GenericTypeResolver#resolveTypeArguments
     */
    public static Class<?>[] getGenericType( Class<?> classInstance, Class<?> classToGetGenerics ) {
        return GenericTypeResolver.resolveTypeArguments( classInstance, classToGetGenerics );
    }

    /**
     *
     * @param classInstance
     * @param classToGetGenerics
     * @param genericPosition
     *
     * @return the generic class of the given param, based on class to get generics and generic position params.
     *
     * @see ObjectUtil#getGenericType
     * @see GenericTypeResolver#resolveTypeArguments
     */
    public static Class<?> getGenericType( Class<?> classInstance, Class<?> classToGetGenerics, int genericPosition ) {
        Class<?>[] typeArguments = getGenericType( classInstance, classToGetGenerics );
        if( typeArguments != null && typeArguments.length >= genericPosition ) {
            return typeArguments[genericPosition];
        }
        throw new IllegalArgumentException( "Could not determine generic type for interface " + classInstance.getName() );
    }
    
    // ... 
}

所以我们可以像下面的例子一样使用(我创建了一个 QueryUtil 来专门处理持久性的东西):

@UtilityClass
public class QueryUtil {

    // ...

    /**
     *
     * @param repositoryClass
     *
     * @return the @Entity class of the given repository param.
     *
     * @see ObjectUtil#getGenericType(Class, Class, int)
     * @see ObjectUtil#getGenericType(Class, Class)
     */
    public static Class<?> getRepositoryEntityType( Class<?> repositoryClass ) {
        return ObjectUtil.getGenericType( repositoryClass, Repository.class, 0 );
    }

}

我希望这仍然有用😊...

这里它被用在定制的审计逻辑中,由方面制作,以便实体被审计。

© www.soinside.com 2019 - 2024. All rights reserved.