海星框架——引入DB模块(多数据源)

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); }}

【海星框架——引入DB模块(多数据源)】海星框架——引入DB模块(多数据源)
文章图片

同样,实体类也要有相同的操作,例如:
海星框架——引入DB模块(多数据源)
文章图片

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 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 = https://www.it610.com/article/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 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 customWrapper = getCustom(componentId, segmentId); if (customDiscoveryResult(customWrapper)) { return new Server(customWrapper.get().getHost(), customWrapper.get().getPort(), customWrapper.get().getContextPath()); } ApiResponse 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 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 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 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 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模块中的相应文件即可

    推荐阅读