栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot + mybatis配置多数据源示例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot + mybatis配置多数据源示例

在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。

代码结构:

简要原理:

1)DatabaseType列出所有的数据源的key---key

2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法

3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType

4)MyBatisConfig中生成2个数据源DataSource的bean---value

5)MyBatisConfig中将1)和4)组成的key-value对写入到DynamicDataSource动态数据源的targetDataSources属性(当然,同时也会设置2个数据源其中的一个为DynamicDataSource的defaultTargetDataSource属性中)

6)将DynamicDataSource作为primary数据源注入到SqlSessionFactory的dataSource属性中去,并且该dataSource作为transactionManager的入参来构造DataSourceTransactionManager

7)使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key,然后再调用mapper层进行相应的操作,建议放在dao层去做(当然也可以使用spring aop+自定注解去做)

注意:在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。

 1、假设有两个数据库,配置如下

application.properties

#the first datasource
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc.username = root
jdbc.password = 123

#the second datasource
jdbc2.driverClassName = com.mysql.jdbc.Driver
jdbc2.url = jdbc:mysql://xxx:3306/mytestdb2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc2.username = root
jdbc2.password = 123

说明:在之前的配置的基础上,只增加了上述的第二个数据源。 

2、DatabaseType

package com.xxx.firstboot.common.datasource;


public enum DatabaseType {
  mytestdb,mytestdb2
}

作用:列举数据源的key。

3、DatabaseContextHolder

package com.xxx.firstboot.common.datasource;


public class DatabaseContextHolder {
  private static final ThreadLocal contextHolder = new ThreadLocal<>();
  
  public static void setDatabaseType(DatabaseType type){
    contextHolder.set(type);
  }
  
  public static DatabaseType getDatabaseType(){
    return contextHolder.get();
  }
}

作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法

4、DynamicDataSource

package com.xxx.firstboot.common.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;


public class DynamicDataSource extends AbstractRoutingDataSource {
  protected Object determineCurrentLookupKey() {
    return DatabaseContextHolder.getDatabaseType();
  }
}

作用:使用DatabaseContextHolder获取当前线程的DatabaseType 

5、MyBatisConfig

package com.xxx.firstboot.common;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.common.datasource.DynamicDataSource;


@Configuration // 该注解类似于spring配置文件
@MapperScan(basePackages = "com.xxx.firstboot.mapper")
public class MyBatisConfig {

  @Autowired
  private Environment env;

  
  @Bean
  public DataSource myTestDbDataSource() throws Exception {
    Properties props = new Properties();
    props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
    props.put("url", env.getProperty("jdbc.url"));
    props.put("username", env.getProperty("jdbc.username"));
    props.put("password", env.getProperty("jdbc.password"));
    return DruidDataSourceFactory.createDataSource(props);
  }

  @Bean
  public DataSource myTestDb2DataSource() throws Exception {
    Properties props = new Properties();
    props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
    props.put("url", env.getProperty("jdbc2.url"));
    props.put("username", env.getProperty("jdbc2.username"));
    props.put("password", env.getProperty("jdbc2.password"));
    return DruidDataSourceFactory.createDataSource(props);
  }

  
  @Bean
  @Primary
  public DynamicDataSource dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,
      @Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
    Map targetDataSources = new HashMap<>();
    targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
    targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource);

    DynamicDataSource dataSource = new DynamicDataSource();
    dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
    dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默认的datasource设置为myTestDbDataSource

    return dataSource;
  }

  
  @Bean
  public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
    SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
    fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
    // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
    fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
    fb.setMapperLocations(
 new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//

    return fb.getObject();
  }

  
  @Bean
  public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
    return new DataSourceTransactionManager(dataSource);
  }

}

作用:

  • 通过读取application.properties文件生成两个数据源(myTestDbDataSource、myTestDb2DataSource)
  • 使用以上生成的两个数据源构造动态数据源dataSource
    • @Primary:指定在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@Autowire注解报错(一般用于多数据源的情况下)
    • @Qualifier:指定名称的注入,当一个接口有多个实现类的时候使用(在本例中,有两个DataSource类型的实例,需要指定名称注入)
    • @Bean:生成的bean实例的名称是方法名(例如上边的@Qualifier注解中使用的名称是前边两个数据源的方法名,而这两个数据源也是使用@Bean注解进行注入的)
  • 通过动态数据源构造SqlSessionFactory和事务管理器(如果不需要事务,后者可以去掉)

 6、使用

ShopMapper:

package com.xxx.firstboot.mapper;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.xxx.firstboot.domain.Shop;

public interface ShopMapper {

  @Select("SELECT * FROM t_shop WHERe id = #{id}")
  @Results(value = { @Result(id = true, column = "id", property = "id"),
     @Result(column = "shop_name", property = "shopName") })
  public Shop getShop(@Param("id") int id);

}

ShopDao:

package com.xxx.firstboot.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.xxx.firstboot.common.datasource.DatabaseContextHolder;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.mapper.ShopMapper;

@Repository
public class ShopDao {
  @Autowired
  private ShopMapper mapper;

  
  public Shop getShop(int id) {
    DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
    return mapper.getShop(id);
  }
}

注意:首先设置了数据源的key,然后调用mapper(在mapper中会首先根据该key从动态数据源中查询出相应的数据源,之后取出连接进行数据库操作)

ShopService:

package com.xxx.firstboot.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xxx.firstboot.dao.ShopDao;
import com.xxx.firstboot.domain.Shop;

@Service
public class ShopService {

  @Autowired
  private ShopDao dao;

  public Shop getShop(int id) {
    return dao.getShop(id);
  }
}

ShopController:

package com.xxx.firstboot.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.service.ShopService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("/shop")
@Api("shopController相关api")
public class ShopController {

  @Autowired
  private ShopService service;

  @ApiOperation("获取shop信息,测试多数据源")
  @RequestMapping(value = "/getShop", method = RequestMethod.GET)
  public Shop getShop(@RequestParam("id") int id) {
    return service.getShop(id);
  }

}

补:其实DatabaseContextHolder和DynamicDataSource完全可以合为一个类

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/147415.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号