Java+Spring+MyBatis实现多数据源的动态切换

固定多数据源切换

    固定多数据源的动态切换,通过自定义注解实现切换,这样在切换数据源时比较灵活,具体的实现方式如下:

    1、配置多数据源

    <!--定义数据源1-->
    <bean id="oracledataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1522:neworcl" /> <property name="username" value="emspdadev" /> <property name="password" value="emspdadev" /> <!-- 初始化连接大小 --> <property name="initialSize" value="0"></property> <!-- 连接池最大数量 --> <property name="maxActive" value="20"></property> <!-- 连接池最大空闲 --> <property name="maxIdle" value="20"></property> <!-- 连接池最小空闲 --> <property name="minIdle" value="1"></property> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="60000"></property> </bean> <!--定义数据源2--> <bean id="mysqldataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/jbpmdb" /> <property name="username" value="root" /> <property name="password" value="123456" /> <!-- 初始化连接大小 --> <property name="initialSize" value="0"></property> <!-- 连接池最大数量 --> <property name="maxActive" value="20"></property> <!-- 连接池最大空闲 --> <property name="maxIdle" value="20"></property> <!-- 连接池最小空闲 --> <property name="minIdle" value="1"></property> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="60000"></property> </bean> <!--动态数据源配置--> <bean id="dataSource" class="com.ssm.datasource.DynamicDataSource"> <!--引入定义好的数据源--> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="oracle" value-ref="oracledataSource" /> <entry key="mysql" value-ref="mysqldataSource" /> </map> </property> <!--定义默认数据源--> <property name="defaultTargetDataSource" ref="oracledataSource" /> </bean> <!--spring和mybatis整合--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:mapping/*.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.ssm.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>

  2、定义注解(注解名为DataSource),用于切换数据源,注解的值只能为上述配置中定义的key(对应于上面配置中定义的oracle、mysql)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) pub lic @interface DataSource { String value(); }
 

    3、根据Sping切面编程,当调用指定的切面类时,解释注解,并根据注解的定义使用对应的数据库

public class DataSourceAspect {
 /**
    * 定义切面,当调用com.ssm.service下的所有类的所有方法前都会执行beforeInvoke方法
    */
    @Pointcut("execution(* com.ssm.service.*.*(..))") public void pointCut(){}; @Before(value = "pointCut()") public void beforeInvoke(JoinPoint joinpoint) { try { String clazzName = joinpoint.getTarget().getClass().getName(); String methodName = joinpoint.getSignature().getName(); Class targetClazz = Class.forName(clazzName); Method[] methods = targetClazz.getMethods(); for(Method method : methods) { if(method.getName().equals(methodName)) { // 首先查看方法是否使用注解 // 如果使用注解,则获取注解定义的值,并根据注解的值设置访问数据库的key if(method.isAnnotationPresent(DataSource.class)) { DataSource dataSource = method.getAnnotation(DataSource.class); DatasourceHolder.setDataType(dataSource.value()); } break; } } } catch (Exception e) { e.printStackTrace(); } } }
 

    4、定义动态切换数据源(继承Spring的AbstractRoutingDataSource)

public class DynamicDataSource extends AbstractRoutingDataSource { /** * 根据DatasourceHolder中DataType的值获取具体的数据源 */ @Override protected Object determineCurrentLookupKey() { return DatasourceHolder.getDataType(); } }
 

    5、数据源切换的使用

@Service
public class IdxServiceImpl implements IIdxSevice { @Autowired private IdxMapper idxMapper; @Override public List<Idx> listIdxInfo() { return null; } /** * 根据注解的配置,会访问oracle对应的数据源 */ @Override @DataSource("oracle") public Map<String,Object> getIdxById(int idxId) { return idxMapper.getIdxById(idxId); } /** * 根据注解的配置,会访问mysql对应的数据源 */ @Override @DataSource("mysql") public Map<String, Object> getJobInfo(int dbId) { return idxMapper.getJobInfo(dbId); } }
 

通过以上的步骤即实现了数据源的动态切换

动态多数据源切换

    对于动态的多数据源,数据源的配置一般不放在配置文件中,因为如果放在配置文件中,每次新增或删除数据源,都需要重启项目,这样的实现方式非常不友好;通常情况向数据源的配置放在数据库中。实现方式如下:

    1、配置数据源,这里配置的数据源用于保存其他数据源的配置信息,今后数据的新增、删除、修改均在该数据库中操作,配置如下:

    <!--定义数据源-->
    <bean id="oracledataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1522:neworcl" /> <property name="username" value="cfgmanage" /> <property name="password" value="cfgmanage" /> <!-- 初始化连接大小 --> <property name="initialSize" value="0"></property> <!-- 连接池最大数量 --> <property name="maxActive" value="20"></property> <!-- 连接池最大空闲 --> <property name="maxIdle" value="20"></property> <!-- 连接池最小空闲 --> <property name="minIdle" value="1"></property> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="60000"></property> </bean> <!--查询动态配置的数据库连接信息--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="oracledataSource" /> </bean> <bean id="dbConfigService" class="com.teamsun.datasource.DBConfigService"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> <!--定义动态数据源--> <bean id="dataSource" class="com.teamsun.datasource.DynamicDataSource"> <property name="masterDataSource" ref="oracledataSource" /> <property name="dbConfigService" ref="dbConfigService" /> </bean> <!--spring和mybatis整合--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:mapper/*.xml" /> <!--<property name="mapperLocations" value="classpath:mapping/*.xml" />--> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.teamsun.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>
 

2、实现查询数据源配置信息的类

public class DBConfigService {

    private JdbcTemplate jdbcTemplate; /** * 查询数据库配置信息 * @param dbName 数据库名称 * @return 数据库配置信息 */ public DBCfg getDBCfg(String dbName) throws Exception { String querySql = "select\n" + " t.db_type as \"dbType\",\n" + " t.db_name as \"dbName\",\n" + " t.db_comment as \"dbCommment\",\n" + " t.db_driver as \"driverClass\",\n" + " t.db_username as \"userName\",\n" + " t.db_password as \"passworld\",\n" + " t.db_url as \"jdbcURL\"" + " from TB_RPT_DBCFG t\n" + " where t.db_name = '" + dbName + "'"; RowMapper<DBCfg> rowMapper = ParameterizedBeanPropertyRowMapper.newInstance(DBCfg.class); DBCfg dbCfg = (DBCfg) jdbcTemplate.queryForObject(querySql, rowMapper); return dbCfg; } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
 

3、实现动态切换数据源

/**
 * <p>动态创建及访问多数据源</p>
 */
public class DynamicDataSource extends AbstractRoutingDataSource{ private DBConfigService dbConfigService; private DataSource masterDataSource; private Map<Object, Object> targetDataSource = new HashMap<Object, Object>(); private static final String DEFAULT_DB_NAME = "dataSource"; // 默认数据库名 private static final Logger LOGGER = Logger.getLogger(DynamicDataSource.class); /** * 创建并获取数据源 * @return */ @Override protected DataSource determineTargetDataSource() { // 获取数据源名称 String dbName = (String) determineCurrentLookupKey(); // 获取默认数据源 if(DEFAULT_DB_NAME.equals(dbName)) { return masterDataSource; } // 创建数据源 DataSource dataSource = (DataSource) targetDataSource.get(dbName); try { if (dataSource == null) { dataSource = getDataSourceByName(dbName); } } catch (Exception e) { e.printStackTrace(); } return dataSource; } /** * 获取数据库名称,可根据获取的数据库名称查询数据库配置信息, * 通过配置信息动态创建数据源 * @return */ @Override protected Object determineCurrentLookupKey() { String dbName = DatasourceHolder.getDBName(); if(StringUtils.isEmpty(dbName)) { dbName = DEFAULT_DB_NAME; } DatasourceHolder.remove(); return dbName; } @Override public void afterPropertiesSet() { } /** * 通过数据库的配置信息获取数据源 * @param dbName 数据库名称 * @return */ public synchronized DataSource getDataSourceByName(String dbName) throws Exception { // 创建数据源 BasicDataSource dataSource = createDataSource(dbName); // 如果创建数据源成功则缓存数据源,避免重复创建相同的数据源 if(dataSource != null) { targetDataSource.put(dbName, dataSource); } return dataSource; } /** * 通过数据库的配置创建数据源 * @param dbName 数据库名称 * @return */ public BasicDataSource createDataSource(String dbName) throws Exception { // 查询动态数据源配置信息 String oriDBName = DatasourceHolder.getDBName(); if(dbConfigService == null) { System.out.println("创建数据源失败[dbCfgService is null......]"); LOGGER.debug("创建数据源失败[dbCfgService is null......]"); } // 通过数据库名称查询相关的数据库配置信息 DatasourceHolder.setDBName(DEFAULT_DB_NAME); DBCfg dbCfg = dbConfigService.getDBCfg(dbName); DatasourceHolder.setDBName(oriDBName); String driver = dbCfg.getDriverClass(); // 数据库驱动 String url = dbCfg.getJdbcURL(); // 数据库连接地址 String username = dbCfg.getUserName(); // 数据库用户名 String password = dbCfg.getPassworld(); // 数据库密码 LOGGER.debug("动态连接的数据库为[" + url + "|" + username + "]"); // 创建数据源 BasicDataSource basicDataSource = new BasicDataSource(); basicDataSource.setDriverClassName(driver); basicDataSource.setUrl(url); basicDataSource.setUsername(username); basicDataSource.setPassword(password); basicDataSource.setTestWhileIdle(true); return basicDataSource; } /** * 如果修改或删除数据源的配置,则需要同步删除缓存的数据源 * @param dbName */ public void removeDataSource(String dbName) { this.targetDataSource.remove(dbName); } public DataSource getMasterDataSource() { return masterDataSource; } public void setMasterDataSource(DataSource masterDataSource) { this.masterDataSource = masterDataSource; } public DBConfigService getDbConfigService() { return dbConfigService; } public void setDbConfigService(DBConfigService dbConfigService) { this.dbConfigService = dbConfigService; } }
 

4、使用动态切换数据源

public class ShowRptServiceImpl implements IShowRptService { private static final Logger LOGGER = Logger.getLogger(ShowRptServiceImpl.class); @Autowired private DBCfgMapper dbCfgMapper; @Autowired private ShowRptInfoMapper showRptInfoMapper; @Override public RptResult queryRptInfo(BaseRpt baseRpt, Map<String, String> params) { // 在调用Mybatis执行数据库之前先选择数据源 DatasourceHolder.setDBName(dbCfg.getDbName()); // 查询报表数据 List<Map<String,Object>> resultList = showRptInfoMapper.queryRptData(querySQL); // 选择数据源 DatasourceHolder.setDBName(dbCfg.getDbName()); // 查询数据数据量 int totalCount = showRptInfoMapper.queryTotalCount(countSQL); RptResult rptResult = new RptResult(); return rptResult; } }
 

通过以上步骤即可实现动态多数据源的动态切换

 

猜你喜欢

转载自www.cnblogs.com/zt2710/p/11116637.html
今日推荐