英文:
Spring Boot Inject Bean Map with Generic Type
问题
Spring Framework 5
我有一个数据库实体的实例,我想要使用该实体的对应存储库。如何将与该实体映射的所有 JPA 存储库注入为键?
例如:
public interface ARepository extends JpaRepository<AEntity, Long> {}
public interface BRepository extends JpaRepository<BEntity, Long> {}
在我的服务组件中:
public class TestService {
@Autowired
private Map<Class, JpaRepository<?, ?>> jpaRepositories;
public <T> List<T> findAll(T entity) {
Class entityClass = entity.getClass();
JpaRepository<?, ?> jpaRepository = jpaRepositories.get(entityClass);
return jpaRepository.findAll();
}
}
我希望 jpaRepositories
被注入这些值:
{ AEntity.class, ARepository }, { BEntity.class, BRepository }
英文:
Spring Framework 5
I have an instance of a Database Entity, and I want to use the corrosponding repository for that entity. How do I inject all JPA Repositories mapped by the entity as the key?
For example:
public interface ARepository extends JpaRepository<AEntity, Long> {}
public interface BRepository extends JpaRepository<BEntity, Long> {}
And in my service component:
public class TestService {
@Autowired
private Map<Class, JpaRepository<?, ?>> jpaRepositories;
public <T> List<T> findAll(T entity) {
Class entityClass = entity.getClass();
JpaRepository<?, ?> jpaRepository = jpaRepositories.get(entityClass);
return jpaRepository.findAll();
}
}
I want jpaRepositories
to be injected with these values:
{ AEntity.class, ARepository }, { BEntity.class, BRepository }
答案1
得分: 0
以下是翻译好的代码部分:
package com.example.repo;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.util.ProxyUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
private static final Logger log = LoggerFactory.getLogger(AppRunner.class);
private final Collection<JpaRepository<?, ?>> jpaRepositories;
@Override
public void run(ApplicationArguments args) throws Exception {
Function<JpaRepository<?, ?>, Class<?>> repositoryClass = r -> {
if (SimpleJpaRepository.class.isAssignableFrom(ProxyUtils.getUserClass(r))) {
Method method = ReflectionUtils.findMethod(SimpleJpaRepository.class, "getDomainClass");
ReflectionUtils.makeAccessible(method);
return (Class<?>) ReflectionUtils.invokeMethod(method, AopProxyUtils.getSingletonTarget(r));
}
return Void.class;
};
Map<Class<?>, JpaRepository<?, ?>> jpaRepositoriesByClass = jpaRepositories.stream().collect(Collectors.toMap(repositoryClass, Function.identity()));
log.info("{}", jpaRepositoriesByClass);
}
}
请注意,我已经将代码部分进行了翻译,确保代码逻辑和结构的一致性。如果您有任何其他问题或需要进一步的帮助,请随时提问。
英文:
That kind of mapping is not available at runtime by default. If you like a hacky approach and REALLY need it, you can create a map like this for JPA repositories:
package com.example.repo;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.util.ProxyUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
private static final Logger log = LoggerFactory.getLogger(AppRunner.class);
private final Collection<JpaRepository<?, ?>> jpaRepositories;
@Override
public void run(ApplicationArguments args) throws Exception {
Function<JpaRepository<?, ?>, Class<?>> repositoryClass = r -> {
if (SimpleJpaRepository.class.isAssignableFrom(ProxyUtils.getUserClass(r))) {
Method method = ReflectionUtils.findMethod(SimpleJpaRepository.class, "getDomainClass");
ReflectionUtils.makeAccessible(method);
return (Class<?>) ReflectionUtils.invokeMethod(method, AopProxyUtils.getSingletonTarget(r));
}
return Void.class;
};
Map<Class<?>, JpaRepository<?, ?>> jpaRepositoriesByClass = jpaRepositories.stream().collect(Collectors.toMap(repositoryClass, Function.identity()));
log.info("{}", jpaRepositoriesByClass);
}
}
答案2
得分: 0
这是我的解决方案:
@Bean
public Map<Class<?>, JpaRepository<?, ?>> jpaRepositoryMap(
List<JpaRepository<?, ?>> jpaRepositories
) {
return jpaRepositories.stream()
.collect(Collectors.toMap(
jpaRepository -> resolveGenericType(jpaRepository, 0),
Function.identity()
));
}
private Class<?> resolveGenericType(Object object, int index) {
return ResolvableType.forClass(JpaRepository.class, object.getClass())
.getGenerics()[index].resolve();
}
Spring使用ResolvableType
作为一个实用工具来获取类的泛型类型。
英文:
Here's my solution:
@Bean
public Map<Class<?>, JpaRepository<?, ?>> jpaRepositoryMap(
List<JpaRepository<?, ?>> jpaRepositories
) {
return jpaRepositories.stream()
.collect(Collectors.toMap(
jpaRepository -> resolveGenericType(jpaRepository, 0),
Function.identity()
));
}
private Class<?> resolveGenericType(Object object, int index) {
return ResolvableType.forClass(JpaRepository.class, object.getClass())
.getGenerics()[index].resolve();
}
Spring has ResolvableType
as a utility to get generic types of classes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论