读写分离:不同的数据源相同的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]]