springboot动态多数据源配置和使用(从数据库读取数据源配置)(三)

上两遍已经描述了动态多数据源的原理和基础实现了,前面的数据源配置都是从application.yml中配置多数据源的,这里再拓展补充一下其他场景,如何读取数据源不从application.yml中配置,实现从数据库中读取数据源配置并动态切换数据源。

一、回顾上篇的动态多数据源配置

上篇:springboot动态多数据源配置和使用(二)

  1. 继承AbstractRoutingDataSource,重写抽象方法determineCurrentLookupKey()
/**
 * 多数据源
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicContextHolder.peek();
    }

}
  1. 注入spring容器
    @Bean
    public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //设置多个数据源的map
        dynamicDataSource.setTargetDataSources(getDynamicDataSource())
        //默认数据源
        DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        return dynamicDataSource;
    }

    private Map<Object, Object> getDynamicDataSource(){
        Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource();
        Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
        dataSourcePropertiesMap.forEach((k, v) -> {
            DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
            targetDataSources.put(k, druidDataSource);
        });

        return targetDataSources;
    }

  1. 可以看到多数据源配置是从application.yml读取出来,然后设置到DynamicDataSource对象里的targetDataSources属性
    dynamicDataSource.setTargetDataSources();

分析

从上面分析可以知道,重要的是targetDataSources这个存放多数据源的map属性。
那么我们只要把targetDataSources这个map由配置文件获取创建dataSource然后放入map改写成由数据读读取出来的配置,再创建dataSource再放入targetDataSources这个map变量就可以实现我们想要的功能了。

二、从数据库获取配置创建数据源

这一步说难也不难,就把数据库的配置保存在数据库的表里面,在切面类切换数据源时读取数据库的配置,然后创建数据源,把创建的数据源通过put方法放入targetDataSources这个map即可,最后在切面类DynamicContextHolder.push(key)改变数据源

但是这样子就很没效率,每次都从数据库读取配置,然后创建dataSource数据源。所以实际上我们是懒加载的模式,再用一个数据源缓存池pool来保存dataSource,如果缓存有了dataSource就不再从数据库读取了,直接从数据源缓存池的pool来获取数据源。

具体实现

1.创建表
CREATE TABLE `oa_quick_knife_datasource` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `datasource_name` varchar(20) DEFAULT '' COMMENT '数据源名称',
  `datasource_url` varchar(200) DEFAULT '' COMMENT '数据源url',
  `datasource_account` varchar(50) DEFAULT '' COMMENT '数据源帐号',
  `datasource_password` varchar(64) DEFAULT '' COMMENT '数据源密码',
  `remark` varchar(200) DEFAULT NULL COMMENT '备注',
  `is_show_type` tinyint(1) DEFAULT NULL COMMENT '数据源可见类型(1-全部人可见,2-部分人可见)',
  `datasource_type` tinyint(1) DEFAULT NULL COMMENT '默认mysql,暂时只支持mysql',
  `update_name` varchar(20) DEFAULT '' COMMENT '更新人',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_code` int(11) unsigned DEFAULT '0' COMMENT '创建人工号',
  `create_name` varchar(20) DEFAULT '' COMMENT '创建人',
  `update_code` int(11) unsigned DEFAULT '0' COMMENT '更新人工号',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `deleted_flag` tinyint(1) DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据源 ';

注:datasource_password这个字段我们不要明文保存数据库密码,我们加密后再放入这个字段里面

2.数据源缓存池

数据源缓存池的类代码

/**
 * 数据源缓存池
 */
public class DataSourceCachePool {
    /** 数据源连接池缓存【本地 class缓存 - 不支持分布式】 */
    private static Map<String, DruidDataSource> dbSources = new HashMap<>();
    private static RedisTemplate<String, Object> redisTemplate;

    private static RedisTemplate<String, Object> getRedisTemplate() {
        if (redisTemplate == null) {
            redisTemplate = (RedisTemplate<String, Object>) SpringContextUtils.getBean("redisTemplate");
        }
        return redisTemplate;
    }

    /**
     * 获取多数据源缓存
     *
     * @param dbKey
     * @return
     */
    public static DynamicDataSourceModel getCacheDynamicDataSourceModel(String dbKey) {
        String redisCacheKey = ConfigConstant.SYS_DYNAMICDB_CACHE + dbKey;
        if (getRedisTemplate().hasKey(redisCacheKey)) {
            String model = (String)getRedisTemplate().opsForValue().get(redisCacheKey);
            return  JSON.parseObject(model,DynamicDataSourceModel.class);
        }
        DatasourceDao datasourceDao = (DatasourceDao)SpringContextUtils.getBean("datasourceDao");
        DynamicDataSourceModel dbSource = datasourceDao.getDynamicDbSourceByCode(dbKey);
        try{
            dbSource.setDbPassword(AesUtil.decryptBySalt(dbSource.getDbPassword(),dbSource.getId()));
        }catch (Exception e){
            throw new RRException("动态数据源密钥解密失败,dbKey:"+dbKey);
        }

        if (dbSource != null) {
            getRedisTemplate().opsForValue().set(redisCacheKey, JSONObject.toJSONString(dbSource));
        }
        return dbSource;
    }

    public static DruidDataSource getCacheBasicDataSource(String dbKey) {
        return dbSources.get(dbKey);
    }

    /**
     * put 数据源缓存
     *
     * @param dbKey
     * @param db
     */
    public static void putCacheBasicDataSource(String dbKey, DruidDataSource db) {
        dbSources.put(dbKey, db);
    }

    /**
     * 清空数据源缓存
     */
    public static void cleanAllCache() {
        //关闭数据源连接
        for(Map.Entry<String, DruidDataSource> entry : dbSources.entrySet()){
            String dbkey = entry.getKey();
            DruidDataSource druidDataSource = entry.getValue();
            if(druidDataSource!=null && druidDataSource.isEnable()){
                druidDataSource.close();
            }
            //清空redis缓存
            getRedisTemplate().delete(ConfigConstant.SYS_DYNAMICDB_CACHE + dbkey);
        }
        //清空缓存
        dbSources.clear();
    }

    public static void removeCache(String dbKey) {
        //关闭数据源连接
        DruidDataSource druidDataSource = dbSources.get(dbKey);
        if(druidDataSource!=null && druidDataSource.isEnable()){
            druidDataSource.close();
        }
        //清空redis缓存
        getRedisTemplate().delete(ConfigConstant.SYS_DYNAMICDB_CACHE + dbKey);
        //清空缓存
        dbSources.remove(dbKey);
    }

}

上面的数据源缓存池主要代码是下面getCacheDynamicDataSourceModel方法的这段

这个方法的逻辑是先从redis缓存数据源配置,redis没有则从数据库获取,以及获取的配置的数据库密码是加密的,所以这里还要再解密

    /**
     * 获取多数据源缓存配置
     *
     * @param dbKey
     * @return
     */
    public static DynamicDataSourceModel getCacheDynamicDataSourceModel(String dbKey) {
        String redisCacheKey = ConfigConstant.SYS_DYNAMICDB_CACHE + dbKey;
        if (getRedisTemplate().hasKey(redisCacheKey)) {
            String model = (String)getRedisTemplate().opsForValue().get(redisCacheKey);
            return  JSON.parseObject(model,DynamicDataSourceModel.class);
        }
        DatasourceDao datasourceDao = (DatasourceDao)SpringContextUtils.getBean("datasourceDao");
        DynamicDataSourceModel dbSource = datasourceDao.getDynamicDbSourceByCode(dbKey);
        try{
            dbSource.setDbPassword(AesUtil.decryptBySalt(dbSource.getDbPassword(),dbSource.getId()));
        }catch (Exception e){
            throw new RRException("动态数据源密钥解密失败,dbKey:"+dbKey);
        }

        if (dbSource != null) {
            getRedisTemplate().opsForValue().set(redisCacheKey, JSONObject.toJSONString(dbSource));
        }
        return dbSource;
    }

还有一个重要的方法,把数据源放入缓存池的dbSource这个map属性里面

    /**
     * put 数据源缓存
     *
     * @param dbKey
     * @param db
     */
    public static void putCacheBasicDataSource(String dbKey, DruidDataSource db) {
        dbSources.put(dbKey, db);
    }
3.再写一个工具类DynamicDBUtil 操作数据源缓存池

这个类的核心方法是getDbSourceByDbKey(),先判断缓存池有没有对应key的数据源,没有则读取数据源配置(先从redis读配置,没有再从数据库读配置),根据配置创建DruidDataSource数据源,再把数据源放入缓存池

getDbSourceByDbKey这个方法的dbKey是指能根据这个key找到数据库对应的记录,这里指该表的id

/**
 * Spring JDBC 实时数据库访问
 *
 */
@Slf4j
public class DynamicDBUtil {

    /**
     * 通过 dbKey ,获取数据源
     *
     * @param dbKey
     * @return
     */
    public static DruidDataSource getDbSourceByDbKey(final String dbKey) {

        //先判断缓存中是否存在数据库链接
        DruidDataSource cacheDbSource = DataSourceCachePool.getCacheBasicDataSource(dbKey);
        if (cacheDbSource != null && !cacheDbSource.isClosed()) {
            log.debug("--------getDbSourceBydbKey------------------从缓存中获取DB连接-------------------");
            return cacheDbSource;
        } else {
            //获取多数据源配置
            DynamicDataSourceModel dbSource = DataSourceCachePool.getCacheDynamicDataSourceModel(dbKey);
            DruidDataSource dataSource = getJdbcDataSource(dbSource);
            if(dataSource!=null && dataSource.isEnable()){
                DataSourceCachePool.putCacheBasicDataSource(dbKey, dataSource);
            }else{
                throw new RRException("动态数据源连接失败,dbKey:"+dbKey);
            }
            log.info("--------getDbSourceBydbKey------------------创建DB数据库连接-------------------");
            return dataSource;
        }
    }

    /**
     * 获取数据源【最底层方法,不要随便调用】
     *
     * @param dbSource
     * @return
     */
    private static DruidDataSource getJdbcDataSource(final DynamicDataSourceModel dbSource) {
        DruidDataSource dataSource = new DruidDataSource();

        String driverClassName = dbSource.getDbDriver();
        String url = dbSource.getDbUrl();
        String dbUser = dbSource.getDbUsername();
        String dbPassword = dbSource.getDbPassword();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        //dataSource.setValidationQuery("SELECT 1 FROM DUAL");
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        dataSource.setBreakAfterAcquireFailure(true);
        dataSource.setConnectionErrorRetryAttempts(0);
        dataSource.setUsername(dbUser);
        dataSource.setMaxWait(60000);
        dataSource.setPassword(dbPassword);

        log.info("******************************************");
        log.info("*                                        *");
        log.info("*====【"+dbSource.getCode()+"】=====Druid连接池已启用 ====*");
        log.info("*                                        *");
        log.info("******************************************");
        return dataSource;
    }

    /**
     * 关闭数据库连接池
     *
     * @param dbKey
     * @return
     */
    public static void closeDbKey(final String dbKey) {
        DruidDataSource dataSource = getDbSourceByDbKey(dbKey);
        try {
            if (dataSource != null && !dataSource.isClosed()) {
                dataSource.getConnection().commit();
                dataSource.getConnection().close();
                dataSource.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    private static JdbcTemplate getJdbcTemplate(String dbKey) {
        DruidDataSource dataSource = getDbSourceByDbKey(dbKey);
        return new JdbcTemplate(dataSource);
    }

    /**
     * 获取连接
     * @param url
     * @param username
     * @param password
     * @param driverName
     * @return
     */
    public static Connection getConn(String url,String username,String password,String driverName) {
        Connection conn = null;
        try {
            Class.forName(driverName);
            conn = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            throw new RRException("无法连接,问题:"+e.getMessage(), e);
        }

        return conn;
    }

    /**
     * 关闭数据库连接
     * @param
     */
    public static void closeConnection(Connection conn) {
        try {
            if(conn!=null){
                conn.close();
            }
        } catch (SQLException e) {
            throw new RRException("close connection failure", e);
        }
    }

}

4. 继承继承AbstractRoutingDataSource,重写抽象方法determineCurrentLookupKey()

这里比上一篇的DynamicDataSource新增了targetDataSources静态变量和setDataSource()静态方法。

targetDataSources这个属性用于存放多数据源

/**
 * 多数据源
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    public static Map<Object, Object> targetDataSources = new ConcurrentHashMap<>(10);

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicContextHolder.peek();
    }

    public static void setDataSource(String dbKey) throws Exception{
        if(!DynamicDataSource.targetDataSources.containsKey(dbKey)){
            DruidDataSource dataSource = DynamicDBUtil.getDbSourceByDbKey(dbKey);
            DynamicDataSource.targetDataSources.put(dbKey,dataSource);
        }
        //切换动态多数据源的dbKey
        DynamicContextHolder.push(dbKey);
        DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringContextUtils.getBean("dynamicDataSource");
        //使得修改后的targetDataSources生效
        dynamicDataSource.afterPropertiesSet();
    }

}
5. 数据源配置类

下面代码通过dynamicDataSource.setTargetDataSources(DynamicDataSource.targetDataSources)把值引用赋值给dynamicDataSource对象(即指向同一块内存,修改了静态变量targetDataSources,就相当于修改了dynamicDataSource对象里面的targetDataSources属性)

@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceConfig {
    @Autowired
    private DynamicDataSourceProperties properties;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DruidDataSource defaultDataSource(DataSourceProperties dataSourceProperties) {
        //默认数据源,通过配置获取创建
        DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
        return defaultDataSource;
    }

    @Bean
    @Primary
    @DependsOn({"defaultDataSource"})
    public DynamicDataSource dynamicDataSource(DruidDataSource defaultDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //设置targetDataSources(通过数据库配置获取,首次创建没有数据源)
        dynamicDataSource.setTargetDataSources(DynamicDataSource.targetDataSources);

        //默认数据源
        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        return dynamicDataSource;
    }

}
6. 再写一个注解以及实现这个注解的切面类就可以了
/**
 * 多数据源注解
 *
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
    String value() default "";
}
/**
 * 多数据源,切面处理类
 *
 */
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(io.renren.datasource.annotation.DataSource) " +
            "|| @within(io.renren.datasource.annotation.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class targetClass = point.getTarget().getClass();
        Method method = signature.getMethod();

        DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
        DataSource methodDataSource = method.getAnnotation(DataSource.class);
        if(targetDataSource != null || methodDataSource != null){
            String value;
            if(methodDataSource != null){
                value = methodDataSource.value();
            }else {
                value = targetDataSource.value();
            }
            //根据dbKey动态设置数据源
            DynamicDataSource.setDataSource(dbKey);
            logger.debug("set datasource is {}", value);
        }

        try {
            return point.proceed();
        } finally {
            DynamicContextHolder.poll();
            logger.debug("clean datasource");
        }
    }
}

到这一步就已完成了,然后把DataSource注解加到service的类或方法上,即可实现操作指定的多数据源。

三、通过接口的入参来指定数据源

上面的注解DataSource的value是写死在代码里面的,但是我们有这样的需求,前端根据接口入参来操作指定数据源的数据。

所以我们在上面的基础上,再改造一下
再写两个注解

/**
 * 多数据源注解-注解数据源的dbKey
 *
 * @author ZhangXinLin
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DbKey {
}

这个自定义注解DbKey,是作用在参数上的,标志该参数是用来指定数据源的dbKey

/**
 * 多数据源注解
 *
 * @author ZhangXinLin
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DynamicDataSource {

}

DynamicDataSource的自定义注解是用在controller的方法上

DynamicDataSource注解的切面类

这个切面类是根据方法的入参dbKey来动态切换数据源,核心代码是调用这行代码
//根据dbKey动态设置数据源
DynamicDataSource.setDataSource(dbKey);

/**
 * @Description: 动态加载多数据源(启动后加载)
 **/
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DynamicDataSourceAspect {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(io.renren.datasource.annotation.DynamicDataSource) " +
            "|| @within(io.renren.datasource.annotation.DynamicDataSource)")
    public void dynamicdataSourcePointCut() {

    }

    @Around("dynamicdataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        //获取参数,根据参数获取数据源
        String dbKey = getDbKey(point);

        if( dbKey != null){
            //根据dbKey动态设置数据源
            DynamicDataSource.setDataSource(dbKey);
        }
        try {
            return point.proceed();
        } finally {
            DynamicContextHolder.poll();
            logger.debug("clean datasource");
        }
    }


    /**
     * 根据@DbKey注解获取数据源的dbKey
     * @param point
     * @return
     */
    private String getDbKey(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Object[] args = point.getArgs();
        String value = null;
        //参数注解,1维是参数,2维是注解
        Annotation[][] annotations = method.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            Object param = args[i];
            Annotation[] paramAnn = annotations[i];
            //参数为空,直接下一个参数
            if (param == null || paramAnn.length == 0) {
                continue;
            }
            for (Annotation pAnnotation : paramAnn) {
                if (pAnnotation.annotationType().equals(DbKey.class)) {
                    value =  param.toString();
                    break;
                }
            }
        }
        return value;
    }
}

然后在controller的方法上加上注解@DynamicDataSource,以及入参加上注解@Dbkey

    /**
     * 查看数据源的所有表列表
     * @param id
     * @return
     */
    @DynamicDataSource
    @RequestMapping("/getTableList/{id}")
    public R getTableList(@PathVariable("id") @DbKey Integer id){
        List<Map<String, Object>> list = datasourceService.queryTableList(id);
        return R.ok().put("list", list);
    }
看看实际效果
  1. 首先我们表的数据源的增删改写一个页面
    在这里插入图片描述

在这里插入图片描述

  1. 在页面调用getTableList接口来切换数据源的数据

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
可以看到,前端页面选择不同的数据库,后端接口就会根据dbKey的入参来动态切换数据源,从而查询出不同数据源的表名列表



源码

源码在一个还没有写完的快速开发平台的项目里面(功能可以在线编写模版,线上配置数据源,不用改代码就可以编写开发模版,生成不同系统的基础代码);
这个项目还没写完,后面写完也会开源出来,所以这里的源码暂时没有

  • 11
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
好的,下面是一个示例代码,使用Spring Boot和Druid连接MySQL多数据源,并且对数据库密码进行了加解密: 1. 添加依赖 在 pom.xml 文件添加以下依赖: ``` <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Spring Boot Starter JDBC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- Druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.23</version> </dependency> <!-- Jasypt --> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> </dependencies> ``` 2. 配置数据源 在 application.yml 文件添加以下配置: ``` # 默认数据源 spring.datasource.url=jdbc:mysql://localhost:3306/db1 spring.datasource.username=root spring.datasource.password=ENC(加密后的密码) spring.datasource.driver-class-name=com.mysql.jdbc.Driver # 第二个数据源 datasource2.url=jdbc:mysql://localhost:3306/db2 datasource2.username=root datasource2.password=ENC(加密后的密码) datasource2.driver-class-name=com.mysql.jdbc.Driver ``` 其,密码字段使用 Jasypt 进行加密,格式为 `ENC(加密后的密码)`。 3. 配置Druid数据源配置文件添加以下配置: ``` # Druid数据源配置 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource # 配置第一个数据源 spring.datasource.druid.initial-size=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-active=20 spring.datasource.druid.max-wait=60000 spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.min-evictable-idle-time-millis=300000 spring.datasource.druid.validation-query=SELECT 1 FROM DUAL spring.datasource.druid.test-while-idle=true spring.datasource.druid.test-on-borrow=false spring.datasource.druid.test-on-return=false spring.datasource.druid.pool-prepared-statements=true spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 spring.datasource.druid.filters=stat,wall,log4j # 配置第二个数据源 datasource2.druid.initial-size=5 datasource2.druid.min-idle=5 datasource2.druid.max-active=20 datasource2.druid.max-wait=60000 datasource2.druid.time-between-eviction-runs-millis=60000 datasource2.druid.min-evictable-idle-time-millis=300000 datasource2.druid.validation-query=SELECT 1 FROM DUAL datasource2.druid.test-while-idle=true datasource2.druid.test-on-borrow=false datasource2.druid.test-on-return=false datasource2.druid.pool-prepared-statements=true datasource2.druid.max-pool-prepared-statement-per-connection-size=20 datasource2.druid.filters=stat,wall,log4j ``` 4. 配置数据源连接池 在配置添加以下代码: ``` @Configuration public class DataSourceConfig { @Bean(name = "dataSource") @Primary @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource dataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "dataSource2") @ConfigurationProperties(prefix = "datasource2") public DruidDataSource dataSource2() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "dataSource1TransactionManager") @Primary public DataSourceTransactionManager dataSourceTransactionManager() { return new DataSourceTransactionManager(dataSource()); } @Bean(name = "dataSource2TransactionManager") public DataSourceTransactionManager dataSource2TransactionManager() { return new DataSourceTransactionManager(dataSource2()); } @Bean(name = "dataSource1SqlSessionFactory") @Primary public SqlSessionFactory dataSourceSqlSessionFactory(@Qualifier("dataSource") DruidDataSource dataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); return sessionFactory.getObject(); } @Bean(name = "dataSource2SqlSessionFactory") public SqlSessionFactory dataSource2SqlSessionFactory(@Qualifier("dataSource2") DruidDataSource dataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper2/*.xml")); return sessionFactory.getObject(); } } ``` 其,`@Primary` 注解表示默认数据源,`@ConfigurationProperties` 注解表示从配置文件读取配置。 5. 配置MyBatis 在 `application.yml` 文件添加以下配置: ``` mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.entity ``` 6. 编写DAO和Mapper 在 `com.example.dao` 包编写DAO和Mapper,例如: ``` @Repository public interface UserDAO { @Select("SELECT * FROM user") @Results(id = "userResultMap", value = { @Result(property = "id", column = "id", id = true), @Result(property = "name", column = "name"), @Result(property = "email", column = "email"), @Result(property = "phone", column = "phone"), @Result(property = "createTime", column = "create_time") }) List<User> list(); } @Mapper public interface UserMapper { @Select("SELECT * FROM user") @ResultMap("userResultMap") List<User> list(); } ``` 7. 使用数据源 在Service使用数据源,例如: ``` @Service public class UserService { @Autowired private UserDAO userDAO; @Autowired private UserMapper userMapper; @Transactional(transactionManager = "dataSource1TransactionManager") public List<User> list() { return userDAO.list(); } @Transactional(transactionManager = "dataSource2TransactionManager") public List<User> list2() { return userMapper.list(); } } ``` 其,`@Transactional` 注解表示开启事务,`transactionManager` 参数指定使用数据源

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值