[英]Spring Data JPA - How to programmatically set JpaRepository base packages
When defining an EntityManager in a Spring Java Config class, I can add the base packages to scan for Entity classes by calling a method on the corresponding builder:在 Spring Java Config 类中定义 EntityManager 时,我可以添加基础包以通过调用相应构建器上的方法来扫描实体类:
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
// Some other configuration here
builder.packages("org.foo.bar", "org.foo.baz");
return builder.build();
}
I need something similar for the places where Spring looks for Repository Interfaces.对于 Spring 寻找存储库接口的地方,我需要类似的东西。 The usual way is using the @EnableJpaRepositories
annotation:通常的方法是使用@EnableJpaRepositories
注解:
@EnableJpaRepositories(basePackages = {"org.foo.barbaz"})
But I would like to have a dynamical way for defining these packages similar to the way above for the Entity locations.但我想有一种动态的方式来定义这些包,类似于上述实体位置的方式。 Something like this:像这样的东西:
public SomeJpaRepositoryFactoryBean entityManagerFactory(JpaRepositoryFactoryBuilder builder) {
// Some other configuration here
builder.packages("org.foo.barbaz");
return builder.build();
}
Is there a way to do this with the current Spring Data JPA release is it simply not meant to be done this way?有没有办法用当前的 Spring Data JPA 版本来做到这一点,难道它根本不打算这样做吗?
You can use @AutoConfigurationPackage
annotation to add your child module's package to scan-packages.您可以使用@AutoConfigurationPackage
批注将您的子模块的包添加到 scan-packages。
@EnableJpaRepositories
from your child module从您的子模块中删除所有@EnableJpaRepositories
Add @AutoConfigurationPackage
class to the top directory of your child module (similar to @SpringBootApplication
, you must put this class to the top-most directory to scan all subpackages):将@AutoConfigurationPackage
类添加到子模块的顶层目录(类似于@SpringBootApplication
,您必须将此类放在最顶层目录才能扫描所有子包):
@AutoConfigurationPackage public class ChildConfiguration { }
Create spring.factories
file under /resources/META-INF/spring.factories
of your child module and add the configuration class:在子模块的/resources/META-INF/spring.factories
下创建spring.factories
文件并添加配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.child.package.ChildConfiguration
Now you can @Autowired
your repository from your core project.现在您可以从您的核心项目@Autowired
您的存储库。 (Tested and worked) (测试和工作)
@EnableJpaRepositories
can be used on more than one @Configuration
class. @EnableJpaRepositories
可用于多个@Configuration
类。 That said, every module can declare its own repositories by having an own configuration class:也就是说,每个模块都可以通过拥有自己的配置类来声明自己的存储库:
@Configuration
@EnableJpaRepositories(basePackages = "package1")
public class ConfigClass1 { /* ... */ }
@Configuration
@EnableJpaRepositories(basePackages = "package2")
public class ConfigClass2 { /* ... */ }
Spring Data JPA then counts in all of them ( package1
and package2
). Spring Data JPA 然后计入所有这些( package1
和package2
)。
Although this is still not a programmatical way, it solves my problem.虽然这仍然不是一种编程方式,但它解决了我的问题。
This problem also puzzled me for almost a week.I debug the "spring application context refresh" code and org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar
line by line and then I solve the problem.这个问题也困扰了我将近一周,我一行一行调试了“spring application context refresh”代码和org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar
,然后我就解决了问题。
I customize my EnableJpaRepository
annotation and JpaRepositoriesRegistrar
,then I can do anything in AbdJpaRepositoriesRegistrar
("abd" is my customized classes's prefix).我自定义了我的EnableJpaRepository
注释和JpaRepositoriesRegistrar
,然后我可以在AbdJpaRepositoriesRegistrar
做任何事情(“abd”是我自定义类的前缀)。
This problem also puzzled me for almost a week.I debug the "spring application context refresh" code and org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar
line by line and then I solve the problem.这个问题也困扰了我将近一周,我一行一行调试了“spring application context refresh”代码和org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar
,然后我就解决了问题。
AbdEnableJpaRepositories.java AbdEnableJpaRepositories.java
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* basePackages
* 复制EnableJpaRepositories,Import自定义的AbdJpaRepositoriesRegistrar.
* Copy from EnableJpaRepositories,Import customized AbdJpaRepositoriesRegistrar.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author ghj
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AbdJpaRepositoriesRegistrar.class)
public @interface AbdEnableJpaRepositories {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
* {@code @EnableJpaRepositories("org.my.pkg")} instead of {@code @EnableJpaRepositories(basePackages="org.my.pkg")}.
*/
String value() default "";
/**
* Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this
* attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
*/
String basePackages() default "";
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
* package of each class specified will be scanned. Consider creating a special no-op marker class or interface in
* each package that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
/**
* Specifies which types are eligible for component scanning. Further narrows the set of candidate components from
* everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters.
*/
Filter[] includeFilters() default {};
/**
* Specifies which types are not eligible for component scanning.
*/
Filter[] excludeFilters() default {};
/**
* Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So
* for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning
* for {@code PersonRepositoryImpl}.
*
* @return
*/
String repositoryImplementationPostfix() default "Impl";
/**
* Configures the location of where to find the Spring Data named queries properties file. Will default to
* {@code META-INF/jpa-named-queries.properties}.
*
* @return
*/
String namedQueriesLocation() default "";
/**
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
* {@link Key#CREATE_IF_NOT_FOUND}.
*
* @return
*/
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
/**
* Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to
* {@link JpaRepositoryFactoryBean}.
*
* @return
*/
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
/**
* Configure the repository base class to be used to create repository proxies for this particular configuration.
*
* @return
* @since 1.9
*/
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
// JPA specific configuration
/**
* Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories
* discovered through this annotation. Defaults to {@code entityManagerFactory}.
*
* @return
*/
String entityManagerFactoryRef() default "entityManagerFactory";
/**
* Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
* discovered through this annotation. Defaults to {@code transactionManager}.
*
* @return
*/
String transactionManagerRef() default "transactionManager";
/**
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
* repositories infrastructure.
*/
boolean considerNestedRepositories() default false;
/**
* Configures whether to enable default transactions for Spring Data JPA repositories. Defaults to {@literal true}. If
* disabled, repositories must be used behind a facade that's configuring transactions (e.g. using Spring's annotation
* driven transaction facilities) or repository methods have to be used to demarcate transactions.
*
* @return whether to enable default transactions, defaults to {@literal true}.
*/
boolean enableDefaultTransactions() default true;
}
AbdJpaRepositoriesRegistrar.java AbdJpaRepositoriesRegistrar.java
import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import java.lang.annotation.Annotation;
class AbdJpaRepositoriesRegistrar extends AbdRepositoryBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return AbdEnableJpaRepositories.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
*/
@Override
protected RepositoryConfigurationExtension getExtension() {
return new JpaRepositoryConfigExtension();
}
}
AbdRepositoryBeanDefinitionRegistrarSupport.java AbdRepositoryBeanDefinitionRegistrarSupport.java
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport;
import org.springframework.data.repository.config.RepositoryConfigurationDelegate;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationUtils;
import org.springframework.util.Assert;
/**
*
* @author ghj
*/
abstract class AbdRepositoryBeanDefinitionRegistrarSupport extends RepositoryBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
// Guard against calls for sub-classes
if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
return;
}
// 使用自定义的AbdAnnotationRepositoryConfigurationSource
AbdAnnotationRepositoryConfigurationSource configurationSource = new AbdAnnotationRepositoryConfigurationSource(
annotationMetadata, getAnnotation(), resourceLoader, environment);
RepositoryConfigurationExtension extension = getExtension();
RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
environment);
delegate.registerRepositoriesIn(registry, extension);
}
}
AbdAnnotationRepositoryConfigurationSource.java. AbdAnnotationRepositoryConfigurationSource.java。 You can override getBasePackages
then you can return any packages you want.你可以覆盖getBasePackages
然后你可以返回任何你想要的包。
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author ghj
*/
class AbdAnnotationRepositoryConfigurationSource extends AnnotationRepositoryConfigurationSource {
private static final String BASE_PACKAGES = "basePackages";
private static final String BASE_PACKAGE_CLASSES = "basePackageClasses";
private final AnnotationMetadata configMetadata;
private final AnnotationAttributes attributes;
private final Environment environment;
AbdAnnotationRepositoryConfigurationSource(AnnotationMetadata metadata, Class<? extends Annotation> annotation, ResourceLoader resourceLoader, Environment environment) {
super(metadata, annotation, resourceLoader, environment);
this.attributes = new AnnotationAttributes(metadata.getAnnotationAttributes(annotation.getName()));
this.configMetadata = metadata;
this.environment = environment;
}
@Override
public Iterable<String> getBasePackages() {
String value = attributes.getStringArray("value")[0];
String basePackages = attributes.getStringArray(BASE_PACKAGES)[0];
Class<?>[] basePackageClasses = attributes.getClassArray(BASE_PACKAGE_CLASSES);
// Default configuration - return package of annotated class
if (StringUtils.isEmpty(value) && StringUtils.isEmpty(basePackages) && basePackageClasses.length == 0) {
String className = configMetadata.getClassName();
return Collections.singleton(ClassUtils.getPackageName(className));
}
String[] packagesFromValue = parsePackagesSpel(value);
String[] packagesFromBasePackages = parsePackagesSpel(basePackages);
Set<String> packages = new HashSet<>();
packages.addAll(Arrays.asList(packagesFromValue));
packages.addAll(Arrays.asList(packagesFromBasePackages));
for (Class<?> typeName : basePackageClasses) {
packages.add(ClassUtils.getPackageName(typeName));
}
return packages;
}
private String[] parsePackagesSpel(String raw) {
if (!raw.trim().startsWith("$")) {
if (StringUtils.isEmpty(raw)) {
return new String[]{};
}
return raw.split(",");
} else {
raw = raw.trim();
String packages = this.environment.getProperty(raw.substring("${".length(), raw.length() - "}".length()));
return packages.split(",");
}
}
}
How to use?Here is configuration file.如何使用?这里是配置文件。 PrimaryJpaConfiguration.java主JpaConfiguration.java
import com.shinow.abd.springjpa2.annotation.AbdEnableJpaRepositories;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@AbdEnableJpaRepositories(
basePackages = "${spring.jpa.base-packages}",
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager"
)
@EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)
public class PrimaryJpaConfiguration implements EnvironmentAware {
private Environment env;
@Bean
@ConditionalOnMissingBean(name = "entityManager")
@Primary
public EntityManager entityManager(@Qualifier("entityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
return entityManagerFactory.getObject().createEntityManager();
}
@Bean
@ConditionalOnMissingBean(name = "entityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSource") DataSource dataSource) {
Map<String, Object> properties = JpaProperties.get("", env);
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaPropertyMap(properties);
entityManagerFactoryBean.setPackagesToScan(env.getProperty("spring.jpa.base-packages").split(","));
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return entityManagerFactoryBean;
}
@Bean
@ConditionalOnMissingBean(name = "dataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConditionalOnMissingBean(name = "transactionManager")
@Primary
public PlatformTransactionManager transactionManager(@Qualifier("entityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
entityManagerFactory.getObject());
return transactionManager;
}
@Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}
You can add spring.jpa.base-packages
config to your application.properties.您可以将spring.jpa.base-packages
配置添加到您的 application.properties。 For example: spring.jpa.base-packages=com.foo.a,com.bar.b
,and the repositories and entities under those packages "com.foo.a" and "com.bar.b" will be added to the spring Ioc container.例如: spring.jpa.base-packages=com.foo.a,com.bar.b
,这些包“com.foo.a”和“com.bar.b”下的存储库和实体将被添加到弹簧 Ioc 容器。
What your looking for is @EntityScan
but it's only available in Spring Boot.您要寻找的是@EntityScan
但它仅在 Spring Boot 中可用。 The configuration you can annotate in Spring Data JPA is documented here https://docs.spring.io/spring-data/jpa/docs/2.0.8.RELEASE/reference/html/#jpa.java-config您可以在 Spring Data JPA 中注释的配置记录在此处https://docs.spring.io/spring-data/jpa/docs/2.0.8.RELEASE/reference/html/#jpa.java-config
@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
class ApplicationConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.acme.domain");
factory.setDataSource(dataSource());
return factory;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
Answer by 高慧觉Spring Data JPA - How to programmatically set JpaRepository base packages worked for me, but I've come up with a simpler and more reliable AnnotationRepositoryConfigurationSource
implementation: just allow Spring Data to collect the packages in its way, and then post-process them and expand property placeholders to package names.高慧觉Spring Data JPA - How to programmatically set JpaRepository 基本包对我有用,但我想出了一个更简单、更可靠的AnnotationRepositoryConfigurationSource
实现:只允许 Spring Data 以它的方式收集包,然后进行后期处理它们并将属性占位符扩展为包名称。
class AbdAnnotationRepositoryConfigurationSource extends AnnotationRepositoryConfigurationSource {
private final Environment environment;
ExpressionsSupportingAnnotationRepositoryConfigurationSource(AnnotationMetadata metadata, Class<? extends Annotation> annotation,
ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) {
super(metadata, annotation, resourceLoader, environment, registry);
this.environment = environment;
}
@Override
public Streamable<String> getBasePackages() {
Streamable<String> rawPackages = super.getBasePackages();
return Streamable.of(() -> rawPackages.stream()
.flatMap(raw -> parsePackagesSpel(raw).stream())
);
}
private List<String> parsePackagesSpel(@Nullable String rawPackage) {
Objects.requireNonNull(rawPackage, "Package specification cannot be null");
if (!rawPackage.trim().startsWith("$")) {
return Collections.singletonList(rawPackage);
}
rawPackage = rawPackage.trim();
String propertyName = rawPackage.substring("${".length(), rawPackage.length() - "}".length());
String packages = this.environment.getProperty(propertyName);
if (!StringUtils.hasText(packages)) {
throw new IllegalStateException(
String.format("Could not resolve the following packages definition: %s", rawPackage));
}
return Arrays.stream(packages.split(","))
.map(String::trim)
.filter(StringUtils::hasText)
.collect(Collectors.toList());
}
}
I've implemented a way to invoke @EnableJpaRepositories
purely programmatically.我已经实现了一种纯粹以编程方式调用@EnableJpaRepositories
的方法。 For the purpose I've created a class which simulates the Annotation data passed to a config class annotated with the @EnableJpaRepositories
annotation.为此,我创建了一个类,该类模拟传递给使用@EnableJpaRepositories
注释注释的配置类的注释数据。 I called my class EnableJpaRepositoriesData
.我称我的班级为EnableJpaRepositoriesData
。 It is confirmed working with Spring 5.1.5, SpringBoot 2.1.3.已确认可与 Spring 5.1.5、SpringBoot 2.1.3 一起使用。 I guess it must be compatible with earlier and later versions alike, with none or very little changes at most.我想它必须与早期和更高版本兼容,最多没有或几乎没有变化。
Below follows the class code, and a sample usage code for it.下面是类代码,以及它的示例使用代码。
EnableJpaRepositoriesData.java:启用JpaRepositoriesData.java:
package org.patladj.jpa.config.multijpa;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Indexed;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurationSelector;
import java.lang.reflect.Method;
import java.util.*;
public class EnableJpaRepositoriesData extends AnnotationMetadataReadingVisitor implements AnnotationMetadata {
private Map<String, ?> data;
private void initIt() throws NoSuchMethodException, ClassNotFoundException {
//################## protected final Set<String> annotationSet = new LinkedHashSet<>(4);
annotationSet.add(Configuration.class.getCanonicalName());
annotationSet.add(EnableTransactionManagement.class.getCanonicalName());
annotationSet.add(EnableJpaRepositories.class.getCanonicalName());
//################## protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<>(4);
metaAnnotationMap.put(Configuration.class.getCanonicalName(),
new LinkedHashSet<>(Arrays.asList(
Component.class.getCanonicalName(),
Indexed.class.getCanonicalName()
)));
metaAnnotationMap.put(EnableTransactionManagement.class.getCanonicalName(),
new LinkedHashSet<>(Arrays.asList(
Import.class.getCanonicalName()
)));
metaAnnotationMap.put(EnableJpaRepositories.class.getCanonicalName(),
new LinkedHashSet<>(Arrays.asList(
Import.class.getCanonicalName()
)));
//################## protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap = new LinkedMultiValueMap<>(4);
attributesMap.put(Configuration.class.getCanonicalName(),
new LinkedList<AnnotationAttributes>() {{
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
put("value", defaultFor(Configuration.class, "value"));
}}));
}});
attributesMap.put(Component.class.getCanonicalName(),
new LinkedList<AnnotationAttributes>() {{
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
put("value", defaultFor(Component.class, "value"));
}}));
}});
attributesMap.put(Indexed.class.getCanonicalName(),
new LinkedList<AnnotationAttributes>() {{
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
}}));
}});
attributesMap.put(EnableTransactionManagement.class.getCanonicalName(),
new LinkedList<AnnotationAttributes>() {{
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
put("order", defaultFor(EnableTransactionManagement.class, "order"));
put("mode", defaultFor(EnableTransactionManagement.class, "mode"));
put("proxyTargetClass", defaultFor(EnableTransactionManagement.class, "proxyTargetClass"));
}}));
}});
attributesMap.put(Import.class.getCanonicalName(),
new LinkedList<AnnotationAttributes>() {{
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
put("value", new Class<?>[]{TransactionManagementConfigurationSelector.class});
}}));
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
put("value", new Class<?>[]{Class.forName("org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar")});
}}));
}});
attributesMap.put(EnableJpaRepositories.class.getCanonicalName(),
new LinkedList<AnnotationAttributes>() {{
add(new AnnotationAttributes(new LinkedHashMap<String, Object>() {{
put("repositoryBaseClass", data.get("repositoryBaseClass"));
put("basePackages", data.get("basePackages"));
put("value", defaultFor(EnableJpaRepositories.class, "value"));
put("excludeFilters", new AnnotationAttributes[]{});
put("includeFilters", new AnnotationAttributes[]{});
put("basePackageClasses", defaultFor(EnableJpaRepositories.class, "basePackageClasses"));
put("bootstrapMode", defaultFor(EnableJpaRepositories.class, "bootstrapMode"));
put("transactionManagerRef", data.get("transactionManagerRef"));
put("considerNestedRepositories", defaultFor(EnableJpaRepositories.class, "considerNestedRepositories"));
put("namedQueriesLocation", defaultFor(EnableJpaRepositories.class, "namedQueriesLocation"));
put("queryLookupStrategy", defaultFor(EnableJpaRepositories.class, "queryLookupStrategy"));
put("entityManagerFactoryRef", data.get("entityManagerFactoryRef"));
put("enableDefaultTransactions", defaultFor(EnableJpaRepositories.class, "enableDefaultTransactions"));
put("repositoryImplementationPostfix", defaultFor(EnableJpaRepositories.class, "repositoryImplementationPostfix"));
put("repositoryFactoryBeanClass", defaultFor(EnableJpaRepositories.class, "repositoryFactoryBeanClass"));
}}));
}});
//##################
}
public EnableJpaRepositoriesData(@Nullable ClassLoader classLoader, Map<String, ?> data) throws NoSuchMethodException, ClassNotFoundException {
super(classLoader);
this.data = data;
this.initIt();
}
private Object defaultFor(Class<?> clazz, String methodName) throws NoSuchMethodException {
Method method = clazz.getDeclaredMethod(methodName);
return method.getDefaultValue();
}
}
Sample usage of the Class to actually invoke enableJpaRepositories programmatically:以编程方式实际调用 enableJpaRepositories 的类的示例用法:
AnnotationMetadata enableJpaRepositoriesData = null;
try {
enableJpaRepositoriesData = new EnableJpaRepositoriesData(this.getClass().getClassLoader(), new HashMap<String, Object>() {{
put("repositoryBaseClass", MyJPAConnectorImpl.class);
put("basePackages", new String[] {
//This is where you set the repositories base packages to scan.
//... Having your best programmatic convenience
"org.patladj.connector.multijpa.common",
"org.patladj.connector.multijpa.anotherrepositoriespackage",
"org.patladj.connector.multijpa.morepackagestoscanforrepositories..."
});
//Ofcourse you need to create by yourself the custom transactionManagerFactory,
// ... entityManagerFactory, JPAVendorAdapter and Datasource beans for your
// ... custom persistence contexts using unique bean names (different than
// ... the default Spring data names: 'entityManagerFactory', etc...)
// ... Then "send" these bean names as references to each and every ...
// and as many as you want persistence contexts programmatically here
put("transactionManagerRef", myCustomPersistanceContextName + "_transactionManager");
put("entityManagerFactoryRef", myCustomPersistanceContextName + "_entityManagerFactory");
}});
} catch (NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(
enableJpaRepositoriesData, EnableJpaRepositories.class, applicationContext, environment, registry
);
RepositoryConfigurationExtension extension = new JpaRepositoryConfigExtension();
RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, applicationContext, environment);
delegate.registerRepositoriesIn(registry, extension);
And finally, in case if you don't know, you can use the following @Configuration class最后,如果您不知道,可以使用以下 @Configuration 类
public class MultiJPABeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware
... to override 2 of its methods to have the post process bean registry
and applicationContext
at your disposal to register bean definitions dynamically. ... 覆盖其 2 个方法,以便您可以使用后期处理 bean registry
和applicationContext
来动态注册 bean 定义。 Methods to override:重写方法:
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException
I referred this post only and created a module aware multi database aware spring data library for mysql(compatible with spring boot) in github.Some application properties need to be added and you are done .我只引用了这篇文章,并在 github 中为 mysql(与 spring boot 兼容)创建了一个模块感知多数据库感知 spring 数据库。需要添加一些应用程序属性,你就完成了。
Documentation and other details could be found at :-可以在以下位置找到文档和其他详细信息:-
https://github.com/yatharthamishra0419/spring-boot-data-multimodule-mysql https://github.com/yatharthamishra0419/spring-boot-data-multimodule-mysql
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.