海星框架——引入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模块(多数据源)_第1张图片
同样,实体类也要有相同的操作,例如:
海星框架——引入DB模块(多数据源)_第2张图片

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 = 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模块中的相应文件即可

你可能感兴趣的:(java)