springboot + mybatis配置多数据源示例

网友投稿 190 2023-06-06


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.usernamehttp:// = root

jdbc2.password = 123

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

2、DatabaseType

package com.xxx.firstboot.common.datasource;

/**

* 列出所有的数据源key(常用数据库名称来命名)

* 注意:

* 1)这里数据源与数据库是一对一的

* 2)DatabaseType中的变量名称就是数据库的名称

*/

public enum DatabaseType {

mytestdb,mytestdb2

}

作用:列举数据源的key。

3、DatabaseContextHolder

package com.xxx.firstboot.common.datasource;

/**

* 作用:

* 1、保存一个线程安全的DatabaseType容器

*/

public class DatabaseContextHolder {

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

public static vouzMDPmDSxJid setDatabaseType(DatabaseType type){

contextHolder.set(type);

}

public staticuzMDPmDSxJ DatabaseType getDatabaseType(){

return contextHolder.get();

}

}

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

4、DynamicDataSource

package com.xxx.firstboot.common.datasource;

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

/**

* 动态数据源(需要继承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;

/**

* springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)

* 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置

*/

@Configuration // 该注解类似于spring配置文件

@MapperScan(basePackages = "com.xxx.firstboot.mapper")

public class MyBatisConfig {

@Autowired

private Environment env;

/**

* 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)

*/

@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);

}

/**

* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错

* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)

*/

@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;

}

/**

* 根据数据源创建SqlSessionFactory

*/

@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;

/**

* 获取shop

*/

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完全可以合为一个类


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring核心IoC和AOP的理解
下一篇:Java使用正则表达式实现找出数字功能示例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~