【Spring boot】IDEA + Maven + Spring Boot + Mybatis 多数据源配置

    公司这周开始安排了某一项目的统计需求,考虑到统计需求是很多客户都提到过的共同需求,我们决定将统计单独拆分出来一个微服务,我们需要统计的数据属于不同项目,但数据表和统计结果都是一样的,所以我们使用spring boot框架支持多数据源,从而满足相同的业务,只需要做一次,就可以满足各个项目的统计需求。

    下面自己就用SpringBoot + Mybatis框架,简单实现了多数据源的支持。项目搭建就不再一一截图了,有了前面几篇框架的实践,应该是很熟悉了。主要是配置的核心代码:

    一. Maven相关依赖

<dependencies>
    <!-- springboot核心包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>
    <!-- springboot-aop包,AOP切面注解,Aspectd等相关注解 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!-- springboot-mybatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>

    <!-- jdbcTemple  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- springboot测试模块包junit -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- 开发测试环境修改文件实时生效包,生产默认不使用 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- mysql数据库连接包 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- mybatis generator 自动生成代码插件 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.2</version>
            <configuration>
                <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
                <overwrite>true</overwrite>
                <verbose>true</verbose>
            </configuration>
        </plugin>
    </plugins>
</build>

    二. application.properties,包含多数据源,mybatis相关配置

server.port=7788
#数据源1 uplus
spring.datasource.db1.url=jdbc:mysql://****:3306/uqierp?useUnicode=true&amp;characterEncoding=UTF-8&amp;autoReconnect=true&amp;zeroDateTimeBehavior=convertToNull&amp;tinyInt1isBit=false&amp;allowMultiQueries=true
spring.datasource.db1.username=****
spring.datasource.db1.password=****
spring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.db1.max-idle=10
spring.datasource.db1.max-wait=10000
spring.datasource.db1.min-idle=5
spring.datasource.db1.initial-size=5

#数据源2 第一联盟
spring.datasource.db2.url=jdbc:mysql://****:3306/uqi_union?useUnicode=true&amp;characterEncoding=UTF-8&amp;autoReconnect=true&amp;zeroDateTimeBehavior=convertToNull&amp;tinyInt1isBit=false&amp;allowMultiQueries=true
spring.datasource.db2.username=****
spring.datasource.db2.password=****
spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.db2.max-idle=10
spring.datasource.db2.max-wait=10000
spring.datasource.db2.min-idle=5
spring.datasource.db2.initial-size=5
#mybatis
mybatis.mapper-locations=classpath*:mapping/*.xml
mybatis.type-aliases-package=com.uqiauto.statistics.model

    三. 数据源相关配置核心代码,AOP实现数据源动态切换

1 DataSourceConfig 多数据源配置类:

@Configuration
public class DataSourceConfig {

    /**
     * 数据源1
     * spring.datasource.db1 :application.properteis中对应属性的前缀
     * @return
     */
    @Bean(name = "datasource1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 数据源1
     * spring.datasource.db2 :application.properteis中对应属性的前缀
     * @return
     */
    @Bean(name = "datasource2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     * @return
     */
    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());
        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap();
        dsMap.put("datasource1", dataSource1());
        dsMap.put("datasource2", dataSource2());

        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }

    /**
     * 配置@Transactional注解
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }
}

2 DataSourceContextHolder 数据源相关操作类:

public class DataSourceContextHolder {
    /**
     * 默认数据源
     */
    public static final String DEFAULT_DS = "datasource1";

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源名
     * @param dbType
     */
    public static void setDB(String dbType) {
        System.out.println("切换到{"+dbType+"}数据源");
        contextHolder.set(dbType);
    }

    /**
     * 获取数据源名
     * @return
     */
    public static String getDB() {
        return (contextHolder.get());
    }

    /**
     * 清除数据源名
     */
    public static void clearDB() {
        contextHolder.remove();
    }
}

3 DS 自定义注解:

/**
 * 自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
    String value() default "datasource1";
}

4 DynamicDataSource 动态获取数据源:

public class DynamicDataSource extends AbstractRoutingDataSource{
    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println("数据源为"+DataSourceContextHolder.getDB());
        return DataSourceContextHolder.getDB();
    }
}

5 DynamicDataSourceAspect AOP实现数据源切换:

@Aspect
@Component
public class DynamicDataSourceAspect {
    @Before("@annotation(DS)")
    public void beforeSwitchDS(JoinPoint point){
        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();
        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
        String dataSource = DataSourceContextHolder.DEFAULT_DS;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(DS.class)) {
                DS annotation = method.getAnnotation(DS.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);
    }

    @After("@annotation(DS)")
    public void afterSwitchDS(JoinPoint point){
        DataSourceContextHolder.clearDB();
    }
}

    有了上面的相关配置,基本上就支持多数据源了,下面就是和业务相关的实现。

    四. 利用Mybatis的自动生成配置,生成相关的Mapper,model类。在生成的基础上,自己写个查询10条订单的实现,新建了DTO,只包含订单中的个别字段,没有返回所有字段,方便测试。相关代码如下:

1 WmsOrdersMapper.xml

<resultMap id="BaseResultMap" type="com.uqiauto.statistics.dto.WmsOrdersDTO" >
    <id column="id_" property="id" jdbcType="INTEGER" />
    <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
    <result column="order_sn" property="orderSn" jdbcType="VARCHAR" />
    <result column="order_money" property="orderMoney" jdbcType="DECIMAL" />
</resultMap>
...
<select id="selectAllOrders" resultMap="BaseResultMap">
    SELECT id_,create_time,order_sn,order_money FROM wms_orders limit 0,10
</select>

2 WmsOrdersMapper

@Mapper
public interface WmsOrdersMapper {
    int deleteByPrimaryKey(Integer id);
    int insert(WmsOrders record);
    ...
    List<WmsOrders> selectAllOrders();
}

3 WmsOrdersService

@Service
public class WmsOrdersService {

    @Resource
    private WmsOrdersMapper wmsOrdersMapper;
    /**
     * 设置了默认数据源为数据源1,不需要加自定义注解
     * @return
     * /
    public List<WmsOrders> selectErpOrders(){
        return wmsOrdersMapper.selectAllOrders();
    }

    /**
     * 使用数据源2查询
     * @return
     */
    @DS("datasource2")
    public List<WmsOrders> selectUnionOrders(){
        return wmsOrdersMapper.selectAllOrders();
    }

}

4 WmsOrdersController

@RestController
@RequestMapping("/statistics")
public class WmsOrdersController {

    @Autowired
    private WmsOrdersService wmsOrdersService;

    @RequestMapping(value = "/getErpOrders",method= RequestMethod.GET)
    public List<WmsOrders> getErpOrders() {
        List<WmsOrders> list = wmsOrdersService.selectErpOrders();
        return list;
    }

    @RequestMapping(value = "/getUnionOrders",method=RequestMethod.GET)
    public List<WmsOrders> getUnionOrders() {
        List<WmsOrders> list = wmsOrdersService.selectUnionOrders();
        return list;
    }
}

    五. 接口测试结果:

这里写图片描述

这里写图片描述

    六. 思考总结:

    这样的一个框架,基本上算是简单支持了项目中的多数据源以及动态切换数据源,底层可以共用一套,满足了不用在各个系统中都单独去实现。但框架还是存在各种问题,例如不支持可配置,再新添一个数据源的情况下,要新增不少代码,想着应该实现所有的业务都只写一套,根据前端传值去数据库中读取数据源的相关信息,而不需要改动任何业务代码。当新增一个数据源,也只需要去数据库中添加数据源的相关信息即可。所以,项目框架还需要进一步优化。

猜你喜欢

转载自blog.csdn.net/u013034223/article/details/80301028