1. 设置数据库属性
包含:数据库名称、用户名、明码
在 business 下创立 dto 文件夹,自定义 SegmentInfo.java,继承 AmqServer
package com.hikvision.isc.module.business.dto;
import com.hikvision.starfish.discovery.AmqServer;
import lombok.Data;
@Data
public class SegmentInfo extends AmqServer {
// 数据库名称
private String dbname;
// 用户名
private String dbusername;
// 明码
private String dbpassword;
}
2. 配置文件中配置数据库信息
以本地数据库和 ibuilding 数据库为例,在配置文件中配置两个数据库的信息
# 多数据库切换——数据库配置
#本地数据库信息
localDburl=jdbc:postgresql://10.196.1.45:7092/sscvhb_sscvhbdb
localUserName=sscvhb_sscvhbdb_user
localingPwd=StgR23C3
#ibuilding 数据库信息
ibuildDburl=jdbc:postgresql://10.196.1.45:7092/ibuilding_ibuildingdb
ibuildingUserName=ibuilding_ibuildingdb_user
ibuildingPwd=I65BRJSB
3. 引入 db 模块
在 business 下引入 db 模块,该模块蕴含五个文件
3.1 DataSourceSwitchAspect
切换数据源
package com.hikvision.isc.module.business.db;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: wangyongqiang13
* @Date: 2020/12/22 9:15
*/
@Component
@Aspect
@Order(-1) // 这是为了保障 AOP 在事务注解之前失效,Order 的值越小, 优先级越高
public class DataSourceSwitchAspect {private final static Logger log = LoggerFactory.getLogger(DataSourceSwitchAspect.class);
// 须要从本地数据库获取的数据,须要在 mapper 文件夹下创立 local 文件夹,并将 mapper 文件定义在 local 文件夹下
@Pointcut("execution(* com.hikvision.isc.sscvhb.module.business.mapper.local..*.*(..))")
private void db1Aspect() {}
// 须要从 ibuilding 数据库获取的数据,须要在 mapper 文件夹下创立 ibuilding 文件夹,并将 mapper 文件定义在 local 文件夹下
@Pointcut("execution(* com.hikvision.isc.sscvhb.module.business.mapper.ibuilding..*.*(..))")
private void db2Aspect() {}
@Before("db1Aspect()")
public void db1() {log.debug("切换到 local 数据源...");
DbContextHolder.setDbType(DBTypeEnum.db1);
}
@Before("db2Aspect()")
public void db2() {log.debug("切换到 ibuilding 数据源...");
DbContextHolder.setDbType(DBTypeEnum.db2);
}
}
同样,实体类也要有雷同的操作,例如:
3.2 DbConfig
用来获取配置文件中的数据库信息
package com.hikvision.isc.module.business.db;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.hikvision.isc.module.business.service.impl.MyHikDiscoveryClientImpl;
import com.hikvision.starfish.discovery.client.impl.HikDiscoveryClientImpl;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* mybatis-plus 扩大配置
*
* @author wangqingxun
* @Date: Created in 2020/12/26
* @since jdk1.8
*/
@Configuration
@MapperScan("com.hikvision.isc.sscvhb.**.mapper*")
public class DbConfig {private final static Logger log = LoggerFactory.getLogger(DbConfig.class);
// 组件示意信息:也是在配置文件中进行配置
@Value("${base.application.componentId}")
private String componentId;
@Value("${base.application.dbSegmentId}")
private String dbSegmentId;
// 配置文件中数据库的信息
@Value("${localDburl}")
private String localDburl;
@Value("${localUserName}")
private String localUserName;
@Value("${localingPwd}")
private String localingPwd;
@Value("${ibuildDburl}")
private String ibuildDburl;
@Value("${ibuildingUserName}")
private String ibuildingUserName;
@Value("${ibuildingPwd}")
private String ibuildingPwd;
@Autowired
private MyHikDiscoveryClientImpl myHikDiscoveryClient;
@Autowired
private HikDiscoveryClientImpl hikDiscoveryClient;
private static String driverClass = "org.postgresql.Driver";
@Bean
public PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();
}
@Bean(name = "db1")
public DataSource db1() {// SegmentInfo segmentInfo = myHikDiscoveryClient.findAmqServer(componentId,dbSegmentId);
/* log.info("初始化新组件数据库 >>>>>>componentId="+componentId+",dbSegmentId="+dbSegmentId+",segmentInfo="+ JSONObject.toJSONString(segmentInfo));
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl("jdbc:postgresql://"+segmentInfo.getIp()+":"+segmentInfo.getPort()+"/"+segmentInfo.getDbName());
dataSource.setUsername(segmentInfo.getDbusername());
dataSource.setPassword(segmentInfo.getDbpassword());*/
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(localDburl);
dataSource.setUsername(localUserName);
dataSource.setPassword(localingPwd);
return dataSource;
}
@Bean(name = "db2")
public DataSource db2() {DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(ibuildDburl);
dataSource.setUsername(ibuildingUserName);
dataSource.setPassword(ibuildingPwd);
return dataSource;
}
/**
* 动静数据源配置
*
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
@Qualifier("db2") DataSource db2
) {DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(db2); // 程序默认数据源,这个要依据程序调用数据源频次,常常把常调用的数据源作为默认
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*Mapper.xml"));
//PerformanceInterceptor(),OptimisticLockerInterceptor()
// 增加分页性能
sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()
});
// sqlSessionFactory.setGlobalConfig(globalConfiguration()); // 正文掉全局配置,因为在 xml 中读取就是全局配置
return sqlSessionFactory.getObject();}
}
3.3 DbContextHolder
package com.hikvision.isc.module.business.db;
public class DbContextHolder {private static final ThreadLocal contextHolder = new ThreadLocal<>();
/**
* 设置数据源
*
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {contextHolder.set(dbTypeEnum.getValue());
}
/**
* 获得以后数据源
*
* @return
*/
public static String getDbType() {return (String) contextHolder.get();}
/**
* 革除上下文数据
*/
public static void clearDbType() {contextHolder.remove();
}
}
3.4 DBTypeEnum
package com.hikvision.isc.module.business.db;
public enum DBTypeEnum {db1("db1"),
db2("db2");
private String value;
DBTypeEnum(String value) {this.value = value;}
public String getValue() {return value;}
}
3.5 DynamicDataSource
获取以后应用哪个数据源
package com.hikvision.isc.module.business.db;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 获得以后应用哪个数据源
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {return DbContextHolder.getDbType();
}
}
4. 主动寻址
在 service.impl 下创立 MyHikDiscoveryClientImpl.java 文件夹,继承 HikDiscoveryClientImpl(在 startfish-discovery-1.8.9.RELEASE.jar 包中)
package com.hikvision.starfish.discovery.client.impl;
import com.hikvision.discovery.exception.ServerNotFoundException;
import com.hikvision.notify.dto.servicechange.ServiceChangeMsgDto;
import com.hikvision.starfish.bic.bo.AmqInfo;
import com.hikvision.starfish.bic.client.ServiceDirectoryClient;
import com.hikvision.starfish.bic.constant.BicConstants;
import com.hikvision.starfish.bic.dto.response.ServiceAddressInfoDto;
import com.hikvision.starfish.bic.dto.response.ServiceInfoDto;
import com.hikvision.starfish.core.response.api.ApiResponse;
import com.hikvision.starfish.discovery.AmqServer;
import com.hikvision.starfish.discovery.Server;
import com.hikvision.starfish.discovery.client.HikDiscoveryClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.env.Environment;
import org.springframework.web.client.HttpServerErrorException;
import java.util.Optional;
/**
* @author dengyishi
* @date 2019/5/25
* @since 1.1.0
*/
public class HikDiscoveryClientImpl implements HikDiscoveryClient {
private static final String SUCCESS = "0";
private ServiceDirectoryClient serviceDirectoryClient;
private CacheManager cacheManager;
Server bicServer;
Server casServer;
Server serviceDirectoryServer;
Server licenseServer;
private HikHttpDiscoveryProperties hikHttpDiscoveryProperties;
public HikDiscoveryClientImpl(ServiceDirectoryClient serviceDirectoryClient, CacheManager cacheManager,
Environment environment, HikHttpDiscoveryProperties hikHttpDiscoveryProperties) {super();
this.serviceDirectoryClient = serviceDirectoryClient;
this.cacheManager = cacheManager;
this.hikHttpDiscoveryProperties = hikHttpDiscoveryProperties;
String bicPort = environment.getRequiredProperty("@bic.bic.port");
bicServer = new Server(environment.getRequiredProperty("@bic.bic.ip"), Integer.valueOf(bicPort), environment.getRequiredProperty("@bic.bic.context"));
String casPort = environment.getRequiredProperty("@bic.cas.port");
casServer = new Server(environment.getRequiredProperty("@bic.cas.ip"), Integer.valueOf(casPort), environment.getRequiredProperty("@bic.cas.context"));
String serviceDirectoryPort = environment.getRequiredProperty("@bic.serviceDirectory.port");
serviceDirectoryServer = new Server(environment.getRequiredProperty("@bic.serviceDirectory.ip"),
Integer.valueOf(serviceDirectoryPort), environment.getRequiredProperty("@bic.serviceDirectory.context"));
String licensePort = environment.getRequiredProperty("@bic.license.port");
licenseServer = new Server(environment.getRequiredProperty("@bic.license.ip"), Integer.valueOf(licensePort), environment.getRequiredProperty("@bic.license.context"));
}
public ServiceInfoDto findService(String componentId, String segmentId) {ApiResponse<ServiceInfoDto> result = serviceDirectoryClient.getServiceInfoV2(componentId, segmentId);
return result.getValidatedData(ServerNotFoundException.class,
"寻址" + componentId + "." + segmentId + "时服务目录产生谬误");
}
@Cacheable(cacheManager = SERVICE_DISCOVERY_CACHE_MANAGER, cacheNames = SERVICE_DISCOVERY_CACHE, key = "#p0+'.'+#p1", unless = "#result==null")
@Override
public AmqServer findAmqServer(String componentId, String mqSegmentId) {AmqServer amqServer = new AmqServer();
ServiceInfoDto serviceInfoDto = findService(componentId, mqSegmentId);
if (serviceInfoDto != null && serviceInfoDto.getAmqInfo() != null) {AmqInfo amqInfo = serviceInfoDto.getAmqInfo();
amqServer.setIp(amqInfo.getIp());
amqServer.setUsername(amqInfo.getUsername());
amqServer.setPassword(amqInfo.getPassword());
amqServer.setSslPort(amqInfo.getSslPort());
amqServer.setPort(amqInfo.getPort());
} else {throw new ServerNotFoundException("寻址" + componentId + "." + mqSegmentId + "失败,无可用服务信息。请查看寻址参数是否正确");
}
return amqServer;
}
@Cacheable(cacheManager = SERVICE_DISCOVERY_CACHE_MANAGER, cacheNames = SERVICE_DISCOVERY_CACHE, key = "#p0+'.'+#p1", unless = "#result==null")
@Override
public Server findHttpServer(String componentId, String segmentId) {Server serverInProperties = localPropertiesDiscovery(componentId, segmentId);
if (serverInProperties != null) {return serverInProperties;}
Optional<HikHttpDiscoveryProperties.Custom> customWrapper = getCustom(componentId, segmentId);
if (customDiscoveryResult(customWrapper)) {return new Server(customWrapper.get().getHost(), customWrapper.get().getPort(), customWrapper.get().getContextPath());
}
ApiResponse<ServiceInfoDto> result;
String portName = getHttpPortName(customWrapper);
try {result = serviceDirectoryClient.getServiceInfo(componentId, segmentId);
} catch (HttpServerErrorException e) {
throw new ServerNotFoundException("寻址" + componentId + "." + segmentId + "时服务目录产生外部谬误,谬误音讯:"
+ e.getMessage() + ",HTTP BODY 为" + e.getResponseBodyAsString());
}
ServiceInfoDto serviceInfoDto = result.getValidatedData(ServerNotFoundException.class,
"寻址" + componentId + "." + segmentId + "时服务目录产生谬误");
if (serviceInfoDto == null) {throw new ServerNotFoundException("未查问到" + componentId + "." + segmentId + "服务地址,请确认组件标识、服务标识是否正确以及相应组件是否已装置");
}
Optional<ServiceAddressInfoDto> address = serviceInfoDto.getAddress().stream()
.filter(addressInfo -> portName.equals(addressInfo.getKey())).findAny();
if (!address.isPresent()) {throw new ServerNotFoundException("寻址" + componentId + "." + segmentId + "失败,无可用 HTTP 端口地址");
}
return new Server(address.get().getIp(), address.get().getPort(), serviceInfoDto.getContext());
}
private Optional<HikHttpDiscoveryProperties.Custom> getCustom(String componentId, String segmentId) {return hikHttpDiscoveryProperties.getCustoms().stream().filter(cp -> (componentId + "." + segmentId).equals(cp.getHostname()) || (componentId + "-" + segmentId).equals(cp.getHostname())).findAny();}
private boolean customDiscoveryResult(Optional<HikHttpDiscoveryProperties.Custom> customWrapper) {if (customWrapper.isPresent()) {HikHttpDiscoveryProperties.Custom custom = customWrapper.get();
if (StringUtils.isNotBlank(custom.getHost()) && custom.getPort() != null) {return true;} else {return false;}
} else {return false;}
}
private String getHttpPortName(Optional<HikHttpDiscoveryProperties.Custom> custom) {if (custom.isPresent()) {return custom.get().getPortName();} else {return BicConstants.KEY_WEB_PORT;}
}
/**
* 间接从组件配置文件中寻址到外围服务
*
* @param componentId
* @param segmentId
* @return
*/
private Server localPropertiesDiscovery(String componentId, String segmentId) {if ("bic".equals(componentId) && componentId.equals(segmentId)) {return bicServer;}
if ("bic".equals(componentId) && "cas".equals(segmentId)) {return casServer;}
if ("bic".equals(componentId) && "serviceDirectory".equals(segmentId)) {return serviceDirectoryServer;}
if ("bic".equals(componentId) && "license".equals(segmentId)) {return licenseServer;}
return null;
}
@Override
public void receiveMsg(ServiceChangeMsgDto serviceChangeMsgDto) {if (serviceChangeMsgDto.getData() == null) {return;}
serviceChangeMsgDto.getData().getIds().forEach(serviceChangeDetailDto -> {String componentId = serviceChangeDetailDto.getComponentId();
String segmentId = serviceChangeDetailDto.getServiceType();
cacheManager.getCache(SERVICE_DISCOVERY_CACHE).evict(componentId + "." + segmentId);
});
}
}
5. 按需更改
有时候可能须要不止两个数据库,比方须要三个、四个等等,这个时候可依据模板批改 db 模块中的相应文件即可