spring boot + mybatis 多数据源配置

来源:互联网 发布:孙俪的淘宝店铺 编辑:程序博客网 时间:2024/05/20 09:48

本项目使用spring-boot + mybatis(tk.mybatis)

项目结构如下

项目结构

1、设置springboot 启动类,禁用自动数据源配置

@SpringBootApplication(scanBasePackages = "me.smallyellow.hhy", exclude = {DataSourceAutoConfiguration.class})@EnableWebPathpublic class Application {    public static void main( String[] args ) {        SpringApplication.run(Application.class, args);    }}

2、在config包下新建MyBatisConfig.java,对数据源进行相关设置,项目启动会走这个类,读取相关配置信息。

@Configurationpublic class MyBatisConfig implements EnvironmentAware{    private Environment environment;    @Override    public void setEnvironment(Environment arg0) {        this.environment = arg0;    }     /**     * 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)     */    @Bean    public DataSource test1DataSource() throws Exception {        Properties props = new Properties();        props.put("driverClassName", environment.getProperty("test1-datasource.driverClassName"));        props.put("url", environment.getProperty("test1-datasource.url"));        props.put("username", environment.getProperty("test1-datasource.username"));        props.put("password", environment.getProperty("test1-datasource.password"));        return DruidDataSourceFactory.createDataSource(props);    }    @Bean    public DataSource test2DataSource() throws Exception {        Properties props = new Properties();        props.put("driverClassName", environment.getProperty("test2-datasource.driverClassName"));        props.put("url", environment.getProperty("test2-datasource.url"));        props.put("username", environment.getProperty("test2-datasource.username"));        props.put("password", environment.getProperty("test2-datasource.password"));        return DruidDataSourceFactory.createDataSource(props);    }    /**     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错     * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)     */    @Bean    @Primary    public DynamicDataSource dataSource(@Qualifier("test1DataSource") DataSource test1DataSource,                                        @Qualifier("test2DataSource") DataSource test2DataSource) {        Map<Object, Object> targetDataSources = new HashMap<>();        targetDataSources.put(DatabaseType.test1, test1DataSource);        targetDataSources.put(DatabaseType.test2, test2DataSource);        DynamicDataSource dataSource = new DynamicDataSource();        dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法        dataSource.setDefaultTargetDataSource(test1DataSource);// 默认的datasource设置为myTestDbDataSource        return dataSource;    }    @Bean    public MapperScannerConfigurer mapperScannerConfigurer() {        MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();        scannerConfigurer.setBasePackage("me.smallyellow.hhy.mapper");        Properties props = new Properties();        props.setProperty("mappers", "tk.mybatis.mapper.common.Mapper");        props.setProperty("IDENTITY", "MYSQL");        props.setProperty("notEmpty", "true");        scannerConfigurer.setProperties(props);        return scannerConfigurer;    }    /**     * 根据数据源创建SqlSessionFactory     */    @Bean    public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();        fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)        // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加        fb.setTypeAliasesPackage("me.smallyellow.hhy.model");// 指定基包        fb.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml"));//        return fb.getObject();    }    /**     * 配置事务管理器     */    @Bean    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {        return new DataSourceTransactionManager(dataSource);    }}

3、设置application.properties

如上代码所示,我们test1-datasource.xx 和 test2-datasource.xx中读取了相关值。故而,我们需要在application.properties设置相关属性值。
test1-datasource.url=jdbc:mysql://localhost:3306/testtest1-datasource.username=roottest1-datasource.password=test1-datasource.driverClassName=com.mysql.jdbc.Drivertest2-datasource.url=jdbc:mysql://localhost:3306/test2test2-datasource.username=roottest2-datasource.password=test2-datasource.driverClassName=com.mysql.jdbc.Driver

以上,成功配置多数据源

解释:MyBatisConfig 中 DynamicDataSource类进行了一个数据源路由的功能,DatabaseType里定义了数据源

public enum DatabaseType {    test1,    test2}
public class DynamicDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        return DatabaseContextHolder.getDatabaseType();    }}

再看DatabaseContextHolder 类,保证了始终只有一个DatabaseType容器

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

至此,已经可以在service中对不同数据源中的数据进行操作了,但如果不是操作默认数据源,需要在操作之前设置数据源

    //数据源1    public List<UserInfo> test() {        List<UserInfo> list= userInfoMapper.selectAll();        return list;    }    //数据源2    public List<UserInfo> test2() {        DatabaseContextHolder.setDatabaseType(DatabaseType.test2);        List<UserInfo> list= userInfoMapper.selectAll();        return list;    }

但这样显得有点麻烦,所以,打算通过注解的方式进行配置

1、新建OtherDataSource.java 内容如下:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface OtherDataSource {    public DatabaseType dataType() default DatabaseType.test2;}

2、我们需要在添加了这个注解的方法执行之前,先自动设置数据源,这里用到了切片的概念。新建DynamicDataSourceAspect.java

@Aspect@Order(-10)//保证该AOP在@Transactional之前执行@Componentpublic class DynamicDataSourceAspect {    @Before("@annotation(otherDataSource)")    public void changeDataSource(JoinPoint point, OtherDataSource otherDataSource){       //获取当前的指定的数据源;        DatabaseType dType = otherDataSource.dataType();        DatabaseContextHolder.setDatabaseType(dType);    }}

添加完这两个类后,我们就可以使用注解的方式切换数据源了

    //数据源1    public List<UserInfo> test() {        List<UserInfo> list= userInfoMapper.selectAll();        return list;    }    //数据源2    @OtherDataSource    public List<App> test2(){        List<App> list = appMapper.selectAll();        return list;    }

详细代码–>前往github


end

原创粉丝点击