[SPRING] Spring Data JPA - 프로그래밍 방식으로 JpaRepository 기본 패키지를 설정하는 방법
SPRINGSpring Data JPA - 프로그래밍 방식으로 JpaRepository 기본 패키지를 설정하는 방법
Spring Java Config 클래스에서 EntityManager를 정의 할 때 해당 빌더에서 메소드를 호출하여 Entity 클래스를 검색하는 기본 패키지를 추가 할 수 있습니다.
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
// Some other configuration here
builder.packages("org.foo.bar", "org.foo.baz");
return builder.build();
}
Spring이 Repository Interfaces를 찾을 곳과 비슷한 것이 필요하다. 일반적인 방법은 @EnableJpaRepositories 주석을 사용하는 것입니다.
@EnableJpaRepositories(basePackages = {"org.foo.barbaz"})
그러나 엔티티 위치에 대해 위의 방법과 비슷한 패키지를 정의 할 수있는 역동적 인 방법을 원합니다. 이 같은:
public SomeJpaRepositoryFactoryBean entityManagerFactory(JpaRepositoryFactoryBuilder builder) {
// Some other configuration here
builder.packages("org.foo.barbaz");
return builder.build();
}
현재 Spring Data JPA 릴리스에서이 작업을 수행 할 수있는 방법이 있습니까?
해결법
-
==============================
1.@AutoConfigurationPackage 주석을 사용하여 하위 모듈의 패키지를 스캔 패키지에 추가 할 수 있습니다.
@AutoConfigurationPackage 주석을 사용하여 하위 모듈의 패키지를 스캔 패키지에 추가 할 수 있습니다.
이제 코어 프로젝트에서 @Autowired 저장소를 사용할 수 있습니다. (테스트 및 작업)
-
==============================
2.@EnableJpaRepositories는 둘 이상의 @Configuration 클래스에서 사용할 수 있습니다. 즉, 모든 모듈은 자체 구성 클래스를 사용하여 자체 저장소를 선언 할 수 있습니다.
@EnableJpaRepositories는 둘 이상의 @Configuration 클래스에서 사용할 수 있습니다. 즉, 모든 모듈은 자체 구성 클래스를 사용하여 자체 저장소를 선언 할 수 있습니다.
@Configuration @EnableJpaRepositories(basePackages = "package1") public class ConfigClass1 { /* ... */ } @Configuration @EnableJpaRepositories(basePackages = "package2") public class ConfigClass2 { /* ... */ }
스프링 데이터 JPA는 그 다음 모든 것을 계산합니다 (package1 및 package2).
이것은 여전히 프로그래밍 방식이 아니지만 내 문제를 해결합니다.
-
==============================
3.이 문제는 또한 거의 일주일 동안 저를 당황 시켰습니다. "spring application context refresh"코드와 org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar를 한 줄씩 디버그 한 다음 문제를 해결합니다.
이 문제는 또한 거의 일주일 동안 저를 당황 시켰습니다. "spring application context refresh"코드와 org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar를 한 줄씩 디버그 한 다음 문제를 해결합니다.
내 EnableJpaRepository 주석 및 JpaRepositoriesRegistrar를 사용자 정의한 다음 AbdJpaRepositoriesRegistrar ( "abd"는 사용자 정의 된 클래스의 접두사)에서 모든 작업을 수행 할 수 있습니다.
이 문제는 또한 거의 일주일 동안 저를 당황 시켰습니다. "spring application context refresh"코드와 org.springframework.data.jpa.repository.config.JpaRepositoriesRegistrar를 한 줄씩 디버그 한 다음 문제를 해결합니다.
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
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
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. 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(","); } } }
사용 방법 구성 파일은 다음과 같습니다. PrimaryJpaConfiguration.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; } }
spring.jpa.base-packages config를 application.properties에 추가 할 수 있습니다. 예 : spring.jpa.base-packages = com.foo.a, com.bar.b 및 패키지 "com.foo.a"및 "com.bar.b"아래의 리포지토리 및 엔티티가 다음에 추가됩니다. 봄 IOC 컨테이너.
-
==============================
4.@EntityScan은 Spring Boot에서만 사용할 수 있습니다. Spring Data JPA에서 주석 처리 할 수있는 설정은 https://docs.spring.io/spring-data/jpa/docs/2.0.8.RELEASE/reference/html/#jpa.java-config에서 확인할 수있다.
@EntityScan은 Spring Boot에서만 사용할 수 있습니다. 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; } }
-
==============================
5.의해 답변을 高 慧 觉 스프링 데이터 JPA는 - 프로그래밍 방식으로 설정 JpaRepository 기본 패키지 나를 위해 일한,하지만 난 간단하고 안정적인 AnnotationRepositoryConfigurationSource 구현을 함께 왔어 : 그냥 봄 데이터 방식으로 패키지를 수집하고 후 처리 속성 자리 표시자를 패키지 이름으로 확장합니다.
의해 답변을 高 慧 觉 스프링 데이터 JPA는 - 프로그래밍 방식으로 설정 JpaRepository 기본 패키지 나를 위해 일한,하지만 난 간단하고 안정적인 AnnotationRepositoryConfigurationSource 구현을 함께 왔어 : 그냥 봄 데이터 방식으로 패키지를 수집하고 후 처리 속성 자리 표시자를 패키지 이름으로 확장합니다.
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()); } }
from https://stackoverflow.com/questions/47635650/spring-data-jpa-how-to-programmatically-set-jparepository-base-packages by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring 애플리케이션에서 junit 테스트를 실행하는 동안 h2 웹 콘솔에 액세스 (0) | 2019.07.11 |
---|---|
[SPRING] Spring Framework - GET과 POST의 차이점 (0) | 2019.07.11 |
[SPRING] JUnit- Spring @Async void 서비스 메소드 테스트 (0) | 2019.07.11 |
[SPRING] 스프링 보안의 여러 antmatchers (0) | 2019.07.10 |
[SPRING] Spring : POST body에서 매개 변수를 얻는 방법? (0) | 2019.07.10 |