首页 > 其他分享 >JPA动态注册多数据源

JPA动态注册多数据源

时间:2023-01-17 09:45:59浏览次数:35  
标签:multiple String JPA 数据源 datasource 注册 test public

背景

目前已经是微服务的天下,但是随着业务需求的日益增长,部分应用还是出现了需要同时连接多个数据源操作数据的技术诉求。

需要对现有的技术架构进行优化升级,查阅了下网上的文章,基本都是照搬的同一篇文章,通过代码的方式同时注册primary和second两个数据源。这种实现方案的技术成本比较低,但是维护成本非常高的,如果我需要同时连接4个、5个甚至更多的数据源,需要不断增加代码注册数据源。

实现方案

比较理想的方式,当然是增加些许配置,就能实现数据源的拓展。本文只讨论如果动态注册JPA的数据源,涉及数据源切换等功能大家可以自行实现。

JPA多数据源需要在Spring IOC中注册DataSource、EntityManagerFactory和JpaTransactionManger,具体实现通过BeanDefinitionRegistryPostProcessor进行bean的动态注册,源码如下:

配置项:

 

spring.datasource.multiple.test.username=root
spring.datasource.multiple.test.password=123456
spring.datasource.multiple.test.url=jdbc:mysql://localhost:3306/tester?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
spring.datasource.multiple.test.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.multiple.test.dialect=org.hibernate.dialect.MySQLDialect
spring.datasource.multiple.test.hikari.pool-name=test-datasource
spring.datasource.multiple.test.hikari.maximum_pool_size=12
#连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒
spring.datasource.multiple.test.hikari.connection_timeout=6000
#最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
spring.datasource.multiple.test.hikari.minimum_idle=10
#空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
# 只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
spring.datasource.multiple.test.hikari.idle_timeout=600000
#连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
spring.datasource.multiple.test.hikari.max_lifetime=1800000
spring.datasource.multiple.test.hikari.login_timeout=500
spring.datasource.multiple.test.hikari.validation_timeout=1000
spring.datasource.multiple.test.hikari.initialization_fail_timeout=1000
#连接测试查询
spring.datasource.multiple.test.hikari.connection_test_query=SELECT 1

 

配置类:

public class MultipleDataSource {

    private String url;

    private String username;

    private String password;

    private String driverClassName;

    private String dialect;

    private HikariConfig hikari;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getDialect() {
        return dialect;
    }

    public void setDialect(String dialect) {
        this.dialect = dialect;
    }

    public HikariConfig getHikari() {
        return hikari;
    }

    public void setHikari(HikariConfig hikari) {
        this.hikari = hikari;
    }

    /**
     * 数据源配置参数校验
     */
    public void validate() {
        Assert.hasText(url, "数据库连接地址不能为空");
        Assert.hasText(username, "数据库用户名不能为空");
        Assert.hasText(password, "数据库密码不能为空");
        Assert.hasText(driverClassName, "数据库驱动类不能为空");
        Assert.hasText(dialect, "数据库方言不能为空");
        Assert.notNull(hikari, "数据库连接池配置不能为空");
    }

}
public class MultipleDataSourceProperties {

    private Map<String, MultipleDataSource> multiple;

    public Map<String, MultipleDataSource> getMultiple() {
        return multiple;
    }

    public void setMultiple(Map<String, MultipleDataSource> multiple) {
        this.multiple = multiple;
    }

    /**
     * 数据源配置参数校验
     */
    public void validate() {
        for (Map.Entry<String, MultipleDataSource> entry : multiple.entrySet()) {
            entry.getValue().validate();
        }
    }

}

动态注册:

@Slf4j
@Configuration
public class MultipleDataSourceRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ApplicationContextAware {

    private static final String DATASOURCE_PREFIX = "spring.datasource";

    public static final String BEAN_DATASOURCE = "%sDataSource";

    public static final String BEAN_ENTITY_MANAGER_FACTORY = "%sEntityManagerFactory";

    public static final String BEAN_TRANSACTION_MANAGER = "%sTransactionManager";

    protected static final String SCAN_PACKAGE = "com.cestc.*.bean.%s";

    protected static final String PERSISTENCE_NAME = "%sPersistenceUnit";

    protected final Map<String, Object> jpaProperties = new HashMap<>(8);

    private Environment environment;

    private ApplicationContext context;

    public MultipleDataSourceRegistryPostProcessor() {
        jpaProperties.put("current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext");
        jpaProperties.put("hibernate.show_sql", "true");
        jpaProperties.put("hibernate.format_sql", "true");
        jpaProperties.put("hibernate.hbm2ddl.auto", "none");
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    }

    /**
     * 注册Hikari数据源
     * @param dataSource    数据源配置信息
     */
    protected BeanDefinition registryDataSource(MultipleDataSource dataSource) {

        return BeanDefinitionBuilder.genericBeanDefinition(HikariDataSource.class, () -> {
            dataSource.getHikari().setJdbcUrl(dataSource.getUrl());
            dataSource.getHikari().setUsername(dataSource.getUsername());
            dataSource.getHikari().setPassword(dataSource.getPassword());
            dataSource.getHikari().setDriverClassName(dataSource.getDriverClassName());

            return new HikariDataSource(dataSource.getHikari());
        }).getBeanDefinition();

    }

    /**
     * 注册EntityManagerFactory
     * @param name          数据源名称
     * @param dataSource    数据源配置信息
     * @param beanFactory
     */
    protected BeanDefinition registryEntityManagerFactory(String name, MultipleDataSource dataSource, DefaultListableBeanFactory beanFactory) {

        HikariDataSource hikariDataSource = beanFactory.getBean(String.format(BEAN_DATASOURCE, name), HikariDataSource.class);
        jpaProperties.put("hibernate.dialect", dataSource.getDialect());

        return BeanDefinitionBuilder.genericBeanDefinition(LocalContainerEntityManagerFactoryBean.class, () -> {
            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
            entityManagerFactoryBean.setDataSource(hikariDataSource);
            entityManagerFactoryBean.setPackagesToScan(String.format(SCAN_PACKAGE, name));
            entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
            entityManagerFactoryBean.setPersistenceUnitName(String.format(PERSISTENCE_NAME, name));

            return entityManagerFactoryBean;
        }).getBeanDefinition();

    }

    /**
     * 注册数据源事务管理器
     * @param name
     * @param beanFactory
     */
    protected BeanDefinition registryTransactionManager(String name, DefaultListableBeanFactory beanFactory) {

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JpaTransactionManager.class);
        builder.addConstructorArgReference(String.format(BEAN_ENTITY_MANAGER_FACTORY, name));
        return builder.getBeanDefinition();

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;

        MultipleDataSourceProperties multipleDataSourceProperties = Binder.get(this.environment).bind(DATASOURCE_PREFIX, MultipleDataSourceProperties.class).get();

        if (Optional.ofNullable(multipleDataSourceProperties).isPresent()
                && !multipleDataSourceProperties.getMultiple().isEmpty()) {

            // 校验数据源参数
            multipleDataSourceProperties.validate();

            for (Map.Entry<String, MultipleDataSource> entry : multipleDataSourceProperties.getMultiple().entrySet()) {
                log.info("注册数据源[{}]", entry.getKey());

                // 注册数据源
                defaultListableBeanFactory.registerBeanDefinition(String.format(BEAN_DATASOURCE, entry.getKey()), registryDataSource(entry.getValue()));
                // 注册entityManagerFactory
                defaultListableBeanFactory.registerBeanDefinition(String.format(BEAN_ENTITY_MANAGER_FACTORY, entry.getKey()), registryEntityManagerFactory(entry.getKey(), entry.getValue(), defaultListableBeanFactory));
                // 注册事务管理器
                defaultListableBeanFactory.registerBeanDefinition(String.format(BEAN_TRANSACTION_MANAGER, entry.getKey()), registryTransactionManager(entry.getKey(), defaultListableBeanFactory));
            }
        }

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

 

欢迎交流

 

标签:multiple,String,JPA,数据源,datasource,注册,test,public
From: https://www.cnblogs.com/changxy-codest/p/17056996.html

相关文章

  • MybatisPlus多数据源
    适用于多种场景:纯粹多库、读写分离、一主多从、混合模式等。场景说明:我们创建两个库,分别为:​​mybatis_plus​​​(以前的库不动)与​​mybatis_plus_1​​​(新建),将mybatis......
  • 服务发现(Discovery)基于Eureka注册中心
    日常开发中,有时候需要将服务的信息暴露给同事,方便调用联调,可以直接针对服务提供一个详细的接口。开启启动类添加:@EnableEurekaClient//启动的时候自动注册到Eureka@Enab......
  • 整合Druid数据源
    整合Druid数据源Druid是阿里巴巴开源平台上一个数据库连接池实现,结合了C3P0,DBCP,PROXOOL等DB池的优点,同时也加入了日志监控。pom依赖<dependency><group......
  • jpa连表查询
    JPAQuery<TSscPlanDayExecutePO>jpaQuery=jpaQueryFactory.selectFrom(planDayExecutePO).leftJoin(tSscPlanDayPO).on(tSscPlanDayPO.id.eq......
  • MeterSphere 接口测试环境/环境组+动态数据源使用
    MeterSphere接口测试支持环境配置,灵活配置运行环境,好多小伙伴在使用过程中,对http配置的搭配以及环境组的使用场景是有疑惑的,下面为大家介绍一下1.环境配置1.1通用配置......
  • Hive 刷题——用户注册、登录、下单综合统计
    需求描述从用户登录明细表(user_login_detail)和订单信息表(order_info)中查询每个用户的注册日期(首次登录日期)、总登录次数以及其在2021年的登录次数、订单数和订单总额。......
  • springboot集成nacos 注册中心
     接上一篇集成配置中心,本文介绍注册中心,目录结构如下在nacosregister的pom.xml文件中添加引用<dependency><groupId>com.alibaba.cloud</groupId>......
  • Spring Boot---(13)Spring Boot 使用JPA访问数据库
    摘要:Spring-data-jpa的强大和方便之处在于:可以仅仅用一层接口,就可以实现对数据库的访问和操作。本文详细介绍了,SpringBoot环境下如何使用Spring-data-jpa来访问和操作数据......
  • 【Dubbo3终极特性】「云原生三中心架构」带你探索Dubbo3体系下的配置中心和元数据中心
    Dubb3的应用级服务发现Dubbo3提供了全新的应用级服务发现模型,该模型在设计与实现上区别于Dubbo2的接口级服务发现模型。概括来说,Dubbo3引入的应用级服务发现主要有以下优......
  • 【Dubbo3终极特性】「云原生三中心架构」带你探索Dubbo3体系下的配置中心和元数据中心
    Dubb3的应用级服务发现Dubbo3提供了全新的应用级服务发现模型,该模型在设计与实现上区别于Dubbo2的接口级服务发现模型。概括来说,Dubbo3引入的应用级服务发现主要有......