JPA多数据源切换 基于 SpringBoot 2.0 分析

JPA自动装载的秘密

在SpringBoot的项目中 会有一个
spring-boot-autoconfigure 的包 通过然后查看 data.jpa 下面的就是有关Spring JPA 自动装配相关的东西


image

image

image

image

上图显示
SpringBoot 自动装配

JpaRepositoriesAutoConfiguration

-->它导入了@Import(JpaRepositoriesAutoConfigureRegistrar.class)

JpaRepositoriesAutoConfigureRegistrar

-----> 中有个类被 EnableJpaRepositories 注解

EnableJpaRepositories -------->

中使用 entityManagerFactoryRef 和 transactionManagerRef 的默认值 分别为entityManagerFactory(entity管理)和transactionManager(事务管理)

image

image
JpaRepositoriesAutoConfiguration ------->

@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
------>

HibernateJpaAutoConfiguration @Import -------> HibernateJpaConfiguration
HibernateJpaConfiguration extends JpaBaseConfiguration ---->
JpaBaseConfiguration
entityManagerFactory 和 transactionManager 这个两个Bean 是通过这样注入进入的

多数据源主要涉及的Bean

Bean

  • DataSource 数据源
  • LocalContainerEntityManagerFactoryBean 管理Entity
  • PlatformTransactionManager 事物
@Bean
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSourceProperties primaryDataSourceProperties() {
    return new DataSourceProperties();
}
读取  spring.datasource.primary 下的数据
@Bean
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
    return primaryDataSourceProperties().initializeDataSourceBuilder().build();
}


EntityManagerFactory  注入  @Qualifier 指定数据源
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
        EntityManagerFactoryBuilder builder,@Qualifier("primaryDataSource") DataSource dataSource) {
    return builder
            .dataSource(dataSource)
            .packages(Order.class)
            .properties(getVendorProperties(dataSource))
  //   . properties(jpaProperties.getHibernateProperties(dataSource)) // 获取并注入hibernate vender相关配置

            .persistenceUnit("primary")
            .build();
}

//TransactionManager  Bean注入
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryEntityManagerFactory") LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager(primaryEntityManagerFactory.getObject());
    return transactionManager;
}

    // repository 扫描的时候,并不确定哪个先扫描,查看源代码
    @EnableJpaRepositories(basePackageClasses = OrderRepository.class,
            entityManagerFactoryRef = "primaryEntityManagerFactory",transactionManagerRef = "primaryTransactionManager")
    @Primary
    public class PrimaryConfiguration {
    }

多数据源配置

spring:datasource:
  primary:
    url: jdbc:mysql://localhost:3306/seller?user=wch&password=wch123&useUnicode=true&characterEncoding=utf-8
  backup:
    url: jdbc:mysql://localhost:3306/seller-backup?user=wch&password=wch123&useUnicode=true&characterEncoding=utf-8
image
  • ● 为一个LocalContainerEntityManagerFactoryBeantransactionManager的bean增加@Primary, 这样如果后面有自动配置的情况,系统能知道默认使用哪个EntityManagerFactoryBean和哪个transactionManager。
  • ● @EnableJpaRepository会自动使用默认名字为entityManagerFactory的LocalContainerEntityManagerFactoryBean 和默认名字为transactionManager的TransactionManager。如果要指定多个EntityManager的情况, 需要在@EnableJpaRepositories上使用entityManagerFactoryRef和transactionManagerRef另外指定不同的LocalContainerEntityManagerFactoryBean和TransactionManager.
  • ● 另外,TransactionManager必须是JpaTransactionManager!必须是JpaTransactionManager! 必须是JpaTransactionManager!入过的坑必须要说3遍。
image

image

主从JPA分别注入 Spring时 如果扫描同一个包下 两个主从只会有一个生效
修改源码 或则 分别放入两个包

修改源代码 黑科技
在本地项目中 建立和要修改的类相同的包名 和相同的类名
源码复制
tips:(当工程中有相同的类的时候,优先使用工程中的类而不再使用依赖包中的类)

在 registerRepositoriesIn 注册Bean的方法中 这段代码中加入

AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
String beanName = configurationSource.generateBeanName(beanDefinition);



AnnotationMetadata metadata = (AnnotationMetadata) configurationSource.getSource();
//判断配置类是否使用primary进行了标注,如果有,就设为primary
if(metadata.hasAnnotation(Primary.class.getName())){
    beanDefinition.setPrimary(true);
}else  if(metadata.hasAnnotation(RepositoryBeanNamePrefix.class.getName())){
    // 再判断是否使用了RepositoryBeanNamePrefix进行了标注,如果有,添加名称前缀
    Map prefixData = metadata.getAnnotationAttributes(RepositoryBeanNamePrefix.class.getName());
    String prefix = (String) prefixData.get("value");
    beanName = prefix + beanName;
}



if (LOGGER.isDebugEnabled()) {
    LOGGER.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName,
            configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName());
}

image

你可能感兴趣的:(JPA多数据源切换 基于 SpringBoot 2.0 分析)