我正在寻找一种从实现 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();
@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;
}
}
我希望这对面临类似问题的其他用户有用。
如果类型参数绑定到接口中的类型,您可以使用此答案获取类型参数:https://stackoverflow.com/a/1901275/66686
Class<T> persistentClass = (Class<T>)
((ParameterizedType)yourRepo.getClass().getGenericSuperclass())
.getActualTypeArguments()[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();
}
}
我通过使用 typetools 解决了这个问题https://github.com/jhalterman/typetools
这是我的代码:
Class<?>[] classes = TypeResolver.resolveRawArguments(JpaRepository.class, jpaRepositoryInstance.getClass());
Class<?> entityClz = classes[0];
我会根据我最近的经验,对Spring Boot中的一些类或接口进行反思,提出自己的看法
对不起我的英语......我来自巴西。
由于我还不能评论答案🤗,我会写在这个答案中分享我对这个问题的经验
这个答案是关于这个堆栈及其版本的:
JDK 11、带有 Spring MVC、Spring Data JPA 和其他库的 Spring Boot 2.7.11。发表于 2023-05-11.
您可以使用 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 );
}
}
我希望这仍然有用😊...
这里它被用在定制的审计逻辑中,由方面制作,以便实体被审计。