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

SpringBoot多数据源(自定义注解,动态数据源,事务实现)

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

SpringBoot多数据源(自定义注解,动态数据源,事务实现)

       本文记录下通过注解动态配置数据源的实现方式,可以是不同的数据库(如读写分离),也可以是mycat代理的分表数据源。废话不多,上干货——

一、数据库配置文件(这里用的是阿波罗配置中心,也可以是application.yml文件)

#mysql本地数据源1
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbc-url=jdbc:mysql://127.0.0.1:3306/mysqlDB1?useUnicode=true&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

#mysql本地数据源2
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbc-url=jdbc:mysql://127.0.0.1:3306/mysqlDB2?useUnicode=true&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

#mycat本地数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbc-url=jdbc:mysql://192.168.0.110:3306/mycatDB?useUnicode=true&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

二、java代码功能部分(根据自己的需求配置实现,不需要mycat分表的可以不配置mycat部分)

   1、首先实现数据源配置

import com.xxx.xxx.aop.DataSourceNames;
import com.xxx.xxx.aop.DynamicDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.*;


@Configuration
@Component
@MapperScan("com.datian.iot.mysql.mapper")
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource")
    public DataSource mysqlDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.mycat")
    public DataSource mycatDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource mysqlDataSource1, DataSource mysqlDataSource2, DataSource mycatDataSource) {
        Map targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.MYSQLDB1, mysqlDataSource1);
        targetDataSources.put(DataSourceNames.MYSQLDB2, mysqlDataSource2);
        targetDataSources.put(DataSourceNames.MYCATDB, mycatDataSource);
        return new DynamicDataSource(mysqlDataSource, targetDataSources);
    }

    
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSource") DataSource ds) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(ds);
        return dataSourceTransactionManager;
    }

}

   2、定义数据源注解常量 

public interface DataSourceNames {
    String MYSQLDB1 = "mysqlDB1";
    String MYSQLDB2 = "mysqlDB2";
    String MYCATDB = "mycatDB";
}

   3、定义数据源注解 

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DataSource {

    String dataSourceName() default DataSourceNames.MYSQLDB1;

}

   4、实现数据源注解 

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Component
@Aspect
@Order(0)
public class DataSourceSection {

    @Pointcut("@annotation(com.datian.iot.aop.DataSource)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();

        DataSource ds = method.getAnnotation(DataSource.class);
        if(ds == null){
            DynamicDataSource.setDataSource(DataSourceNames.MYSQLDB1);
        }else {
            DynamicDataSource.setDataSource(ds.dataSourceName());
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }

}

    5、动态数据源实现

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

import javax.sql.DataSource;
import java.util.Map;


public class DynamicDataSource extends AbstractRoutingDataSource {

    // 用来保存数据源与获取数据源
    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }

}

三、测试部分

    1、编写启动类(注意添加@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })屏蔽springboot的数据源自动配置)

import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
@EnableScheduling
@EnableApolloConfig
@MapperScan("com.xxx.xxx.mysql.mapper*")
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

}

    2、常规测试

import com.xxx.xxx.mysql.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;



@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private TestService testService;

    
    @PostMapping("manualOperation")
    public List> manualOperation(String table, String imei) {
        return testService.manualOperation(table, imei);
    }

    
    @PostMapping("manualUpdate")
    public Map manualUpdate(String table, String imei) {
        return testService.manualUpdate(table, imei);
    }

}
import java.util.List;
import java.util.Map;


public interface TestService {

    
    List> manualOperation(String table, String imei);

    
    Map manualUpdate(String table, String imei);

}
import com.xxx.xxx.aop.DataSource;
import com.xxx.xxx.aop.DataSourceNames;
import com.xxx.xxx.mysql.TestService;
import com.xxx.xxx.mysql.mapper.TestMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestMapper testMapper;

    // 需要哪个数据源直接添加注解即可,不加默认用MYSQLDB1
    @Override
    @DataSource(dataSourceName = DataSourceNames.MYSQLDB2)
    public List> manualOperation(String table, String imei) {
        return testMapper.manualOperation(table, imei);
    }

    // 测试事务
    @Override
    @Transactional
    @DataSource(dataSourceName = DataSourceNames.MYCATDB)
    public Map manualUpdate(String table, String imei) {
        testMapper.manualDelete(table, imei);
        testMapper.manualUpdate(table, imei);
        Map map = new HashMap<>();
        map.put("result", 2/0);
        return map;
    }
}
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;


public interface TestMapper {

    List> manualOperation(@Param("table") String table, @Param("imei") String imei);

    int manualDelete(@Param("table") String table, @Param("imei") String imei);

    int manualUpdate(@Param("table") String table, @Param("imei") String imei);

}