多源数据库读写分离

读写分离:不同的数据源相同的repositories
(同一个repository有时连接到读库,又是连接到写库)
解决方案:
添加额外的接口,继承之前的接口
1.定义两个完全相同功能的接口,放在不同的包路径下面
2.修改源代码

在这里插入图片描述
OrderRepository

 package com.imooc.seller.repositories;
    
    import com.imooc.entity.Order;
    import com.imooc.entity.VerificationOrder;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    import org.springframework.data.jpa.repository.Query;
    
    import java.util.Date;
    import java.util.List;
    
    /**
     * 订单管理,继承接口
     */
    
    public interface OrderRepository extends JpaRepository<Order, String> , JpaSpecificationExecutor<Order> {
        @Query(value = "SELECT CONCAT_WS('|', order_id,outer_order_id,chan_id,chan_user_id,product_id,order_type,amount,DATE_FORMAT( create_at,'%Y-%m-%d %H:%i:%s')) FROM order_t WHERE order_status = 'success' AND chan_id = ?1 AND create_at >= ?2 AND create_at < ?3",nativeQuery = true)
        List<String> queryVerificationOrders(String chanId, Date start, Date end);
    
    }

**BackupOrderRepository**

package com.imooc.seller.repositoriesbackup;

import com.imooc.seller.repositories.OrderRepository;

/**
 * 读库,repositories
 */
public interface BackupOrderRepository extends OrderRepository {
}

在这里插入图片描述
在这里插入图片描述
修改源码:

在这里插入图片描述
在java下添加package org.springframework.data.repository.config;

RepositoryBeanNamePrefix

package org.springframework.data.repository.config;

import java.lang.annotation.*;

/**
 * repository bean 名称的前缀
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RepositoryBeanNamePrefix {
    String value();
}

RepositoryConfigurationDelegate

/*
 * Copyright 2014-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.repository.config;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.util.Assert;

/**
 * Delegate for configuration integration to reuse the general way of detecting repositories. Customization is done by
 * providing a configuration format specific {@link RepositoryConfigurationSource} (currently either XML or annotations
 * are supported). The actual registration can then be triggered for different {@link RepositoryConfigurationExtension}
 * s.
 *
 * @author Oliver Gierke
 * @author Jens Schauder
 */
public class RepositoryConfigurationDelegate {

    private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryConfigurationDelegate.class);

    private static final String REPOSITORY_REGISTRATION = "Spring Data {} - Registering repository: {} - Interface: {} - Factory: {}";
    private static final String MULTIPLE_MODULES = "Multiple Spring Data modules found, entering strict repository configuration mode!";

    static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";

    private final RepositoryConfigurationSource configurationSource;
    private final ResourceLoader resourceLoader;
    private final Environment environment;
    private final boolean isXml;
    private final boolean inMultiStoreMode;

    /**
     * Creates a new {@link RepositoryConfigurationDelegate} for the given {@link RepositoryConfigurationSource} and
     * {@link ResourceLoader} and {@link Environment}.
     *
     * @param configurationSource must not be {@literal null}.
     * @param resourceLoader must not be {@literal null}.
     * @param environment must not be {@literal null}.
     */
    public RepositoryConfigurationDelegate(RepositoryConfigurationSource configurationSource,
                                           ResourceLoader resourceLoader, Environment environment) {

        this.isXml = configurationSource instanceof XmlRepositoryConfigurationSource;
        boolean isAnnotation = configurationSource instanceof AnnotationRepositoryConfigurationSource;

        Assert.isTrue(isXml || isAnnotation,
                "Configuration source must either be an Xml- or an AnnotationBasedConfigurationSource!");
        Assert.notNull(resourceLoader, "ResourceLoader must not be null!");

        this.configurationSource = configurationSource;
        this.resourceLoader = resourceLoader;
        this.environment = defaultEnvironment(environment, resourceLoader);
        this.inMultiStoreMode = multipleStoresDetected();
    }

    /**
     * Defaults the environment in case the given one is null. Used as fallback, in case the legacy constructor was
     * invoked.
     *
     * @param environment can be {@literal null}.
     * @param resourceLoader can be {@literal null}.
     * @return
     */
    private static Environment defaultEnvironment(@Nullable Environment environment,
                                                  @Nullable ResourceLoader resourceLoader) {

        if (environment != null) {
            return environment;
        }

        return resourceLoader instanceof EnvironmentCapable ? ((EnvironmentCapable) resourceLoader).getEnvironment()
                : new StandardEnvironment();
    }

    /**
     * Registers the found repositories in the given {@link BeanDefinitionRegistry}.
     *
     * @param registry
     * @param extension
     * @return {@link BeanComponentDefinition}s for all repository bean definitions found.
     */
    public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
                                                                RepositoryConfigurationExtension extension) {

        extension.registerBeansForRoot(registry, configurationSource);

        RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader,
                environment);
        List<BeanComponentDefinition> definitions = new ArrayList<>();

        for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
                .getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {

            BeanDefinitionBuilder definitionBuilder = builder.build(configuration);

            extension.postProcess(definitionBuilder, configurationSource);

            if (isXml) {
                extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
            } else {
                extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
            }

            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<String,Object> 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());
            }

            beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());

            registry.registerBeanDefinition(beanName, beanDefinition);
            definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
        }

        return definitions;
    }

    /**
     * Scans {@code repository.support} packages for implementations of {@link RepositoryFactorySupport}. Finding more
     * than a single type is considered a multi-store configuration scenario which will trigger stricter repository
     * scanning.
     *
     * @return
     */
    private boolean multipleStoresDetected() {

        boolean multipleModulesFound = SpringFactoriesLoader
                .loadFactoryNames(RepositoryFactorySupport.class, resourceLoader.getClassLoader()).size() > 1;

        if (multipleModulesFound) {
            LOGGER.info(MULTIPLE_MODULES);
        }

        return multipleModulesFound;
    }
}

验证:在
在这里插入图片描述
添加下面的代码,验证OrderRepository会执行不同的数据源

在这里插入图片描述

@EnableJpaRepositories(basePackageClasses = OrderRepository.class,
            entityManagerFactoryRef = "backupEntityManagerFactory",transactionManagerRef = "backupTransactionManager")
    @RepositoryBeanNamePrefix("read")
    public class ReadConfiguration {
    }

不要在意圈中的代码为红色,因为有一个判断语句执行成功,才会出现readOrderRepository这个bean,才会触发名称限定
在这里插入图片描述

验证代码:

@Test
    public void queryOrder1(){
        System.out.println(orderRepository.findAll());
        //System.out.println(backupOrderRepository.findAll());
        System.out.println(readOrderRepository.findAll());
    }

执行结果:

2019-08-12 21:50:54.370 INFO 8392 — [ Test worker] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select order0_.order_id as order_id1_1_, order0_.amount as amount2_1_, order0_.chan_id as chan_id3_1_, order0_.chan_user_id as chan_use4_1_, order0_.create_at as create_a5_1_, order0_.memo as memo6_1_, order0_.order_status as order_st7_1_, order0_.order_type as order_ty8_1_, order0_.outer_order_id as outer_or9_1_, order0_.product_id as product10_1_, order0_.update_at as update_11_1_ from order_t order0_
[com.imooc.entity.Order@c00466[orderId=14ef49d1d3f04851bdb6d0ca62f96790,chanId=10001,chanUserId=001,orderType=APPLY,productId=002,amount=100.000,outerOrderId=000003,orderStatus=SUCCESS,memo=,createAt=2018-12-31 01:15:48.0,updateAt=2019-08-02 19:04:19.0], com.imooc.entity.Order@f0665f[orderId=169c5ac94c594e2b834a30db2bbd4f3d,chanId=10001,chanUserId=002,orderType=APPLY,productId=002,amount=100.000,outerOrderId=000004,orderStatus=SUCCESS,memo=,createAt=2018-12-31 23:15:48.0,updateAt=2019-08-02 19:27:02.0], com.imooc.entity.Order@d48d7e[orderId=e9d4042073e64bac835640c15abae170,chanId=45555,chanUserId=333333,orderType=APPLY,productId=002,amount=900.000,outerOrderId=0003,orderStatus=SUCCESS,memo=,createAt=2018-12-31 03:48:51.0,updateAt=2019-08-02 10:50:04.0]]
2019-08-12 21:50:54.951 INFO 8392 — [ Test worker] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select order0_.order_id as order_id1_1_, order0_.amount as amount2_1_, order0_.chan_id as chan_id3_1_, order0_.chan_user_id as chan_use4_1_, order0_.create_at as create_a5_1_, order0_.memo as memo6_1_, order0_.order_status as order_st7_1_, order0_.order_type as order_ty8_1_, order0_.outer_order_id as outer_or9_1_, order0_.product_id as product10_1_, order0_.update_at as update_11_1_ from order_t order0_
[com.imooc.entity.Order@eb212b[orderId=e9d4042073e64bac835640c15abae170,chanId=45555,chanUserId=333333,orderType=APPLY,productId=002,amount=900.000,outerOrderId=0003,orderStatus=SUCCESS,memo=,createAt=2018-12-31 03:48:51.0,updateAt=2019-08-02 10:50:04.0]]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值