• springboot配置hibernate jpa多数据源


    这里我用的springboot项目,配置文件yml文件配置,gradle配置jar包依赖。

    找了一天资料,终于整好了多数据源,步骤如下:

    application.yml:

     1 spring:
     2   datasource:
     3     driver-class-name: com.mysql.jdbc.Driver
     4     url: jdbc:mysql://localhost:3306/base?characterEncoding=utf8&useSSL=false          #5.5.45+, 5.6.26+ and 5.7.6+版本的mysql需要设置useSSL=false
     5     username: root
     6     password: 123456
     7     maximum-pool-size: 100            #datasource公共配置start
     8     max-idle: 10
     9     max-wait: 10000
    10     min-idle: 5
    11     initial-size: 5
    12     validation-query: SELECT 1
    13     test-on-borrow: false
    14     test-while-idle: true
    15     time-between-eviction-runs-millis: 18800           #datasource公共配置end
    16   jpa:
    17     database: MYSQL
    18     show-sql: true
    19     hibernate:
    20       ddl-auto: update   #validate | update | create | create-drop
    21       naming:
    22         strategy: org.hibernate.cfg.DefaultNamingStrategy
    23     properties:
    24       hibernate:
    25         dialect: org.hibernate.dialect.MySQL5Dialect  #Hibernate方言
    26   freemarker:
    27     allow-request-override: false
    28     allow-session-override: false
    29     cache: false
    30     charset: UTF-8
    31     check-template-location: true
    32     content-type: text/html
    33     enabled: true
    34     expose-request-attributes: false
    35     expose-session-attributes: false
    36     expose-spring-macro-helpers: true
    37     prefer-file-system-access: true
    38     suffix: .html      #html静态页面
    39     template-loader-path: classpath:/templates/       #模板路径
    40     settings:
    41       template_update_delay: 0
    42       default_encoding: UTF-8
    43       classic_compatible: true
    44       date_format: yyyy-MM-dd
    45       time_format: HH:mm:ss
    46       datetime_format: yyyy-MM-dd HH:mm:ss
    47 custom:
    48   datasource:
    49     names: ds1    #若需要添加其他数据源,可以直接在此处添加,用逗号隔开例如:(ds2,ds3),相应的下面的数据库配置只需要添加一个配置就可以了
    50     ds1:
    51       driver-class-name: com.mysql.jdbc.Driver
    52       url: jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&useSSL=false  #5.5.45+, 5.6.26+ and 5.7.6+版本的mysql需要设置useSSL=false
    53       username: root
    54       password: 123456

    build.gradle添加相关依赖:

    1     // mysql依赖
    2     compile('mysql:mysql-connector-java')
    3     // druid依赖
    4     compile('com.alibaba:druid:1.0.15')
    5     // jpa依赖
    6     compile('org.springframework.boot:spring-boot-starter-data-jpa')
    7     


    下面是数据源的配置:

     1 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     2 
     3 /**
     4  * 动态数据源
     5   */
     6 public class DynamicDataSource extends AbstractRoutingDataSource{
     7 
     8     /*
     9      * 代码中的determineCurrentLookupKey方法取得一个字符串,
    10      * 该字符串将与配置文件中的相应字符串进行匹配以定位数据源,配置文件,即applicationContext.xml文件中需要要如下代码:(non-Javadoc)
    11      */
    12     @Override
    13     protected Object determineCurrentLookupKey() {
    14         return DynamicDataSourceContextHolder.getDataSourceType();
    15     }
    16 }

     1 import org.aspectj.lang.JoinPoint;
     2 import org.aspectj.lang.annotation.After;
     3 import org.aspectj.lang.annotation.Aspect;
     4 import org.aspectj.lang.annotation.Before;
     5 import org.springframework.core.annotation.Order;
     6 import org.springframework.stereotype.Component;
     7 
     8 /**
     9  * 切换数据源Advice
    10   */
    11 @Aspect
    12 @Order(-10)//保证该AOP在@Transactional之前执行
    13 @Component
    14 public class DynamicDataSourceAspect {
    15 
    16 
    17     /*
    18      * @Before("@annotation(ds)")
    19      * 的意思是:
    20      *
    21      * @Before:在方法执行之前进行执行:
    22      * @annotation(targetDataSource):
    23      * 会拦截注解targetDataSource的方法,否则不拦截;
    24      */
    25     @Before("@annotation(targetDataSource)")
    26     public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable {
    27         //获取当前的指定的数据源;
    28         String dsId = targetDataSource.value();
    29         //如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。
    30         if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
    31             System.err.println("数据源[{}]不存在,使用默认数据源 > {}"+targetDataSource.value()+point.getSignature());
    32         } else {
    33             System.out.println("Use DataSource : {} > {}"+targetDataSource.value()+point.getSignature());
    34             //找到的话,那么设置到动态数据源上下文中。
    35             DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
    36         }
    37     }
    38 
    39     @After("@annotation(targetDataSource)")
    40     public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
    41         System.out.println("Revert DataSource : {} > {}"+targetDataSource.value()+point.getSignature());
    42         //方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
    43         DynamicDataSourceContextHolder.clearDataSourceType();
    44     }
    45 
    46 
    47 }

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 /**
     5  * 动态数据源上下文
     6   */
     7 public class DynamicDataSourceContextHolder {
     8     /*
     9     * 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
    10     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
    11     */
    12     private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    13 
    14     /*
    15      * 管理所有的数据源id;
    16      * 主要是为了判断数据源是否存在;
    17      */
    18     public static List<String> dataSourceIds = new ArrayList<>();
    19 
    20 
    21     /**
    22      * 使用setDataSourceType设置当前的
    23      */
    24     public static void setDataSourceType(String dataSourceType){
    25         contextHolder.set(dataSourceType);
    26     }
    27 
    28 
    29     /**
    30      * 获取当前线程中的数据源
    31      */
    32     public static String getDataSourceType(){
    33         return contextHolder.get();
    34     }
    35 
    36     /**
    37      * 删除当前线程池中的数据源
    38       */
    39     public static void clearDataSourceType(){
    40         contextHolder.remove();
    41     }
    42 
    43     public static boolean containsDataSource(String dataSourceId){
    44         return dataSourceIds.contains(dataSourceId);
    45     }
    46 
    47 }

    import java.util.HashMap;
    import java.util.Map;
    import javax.sql.DataSource;
    
    import org.springframework.beans.MutablePropertyValues;
    import org.springframework.beans.PropertyValues;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.GenericBeanDefinition;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    import org.springframework.boot.bind.RelaxedDataBinder;
    import org.springframework.boot.bind.RelaxedPropertyResolver;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.core.convert.support.DefaultConversionService;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * 动态数据源注册
     */
    public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
        //如配置文件中未指定数据源类型,使用该默认值
        private static final Object            DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
        private              ConversionService conversionService       = new DefaultConversionService();
        private PropertyValues dataSourcePropertyValues;
    
        // 默认数据源
        private DataSource defaultDataSource;
    
        private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();
    
        /**
         * 加载多数据源配置
         */
        @Override
        public void setEnvironment(Environment environment) {
            System.out.println("DynamicDataSourceRegister.setEnvironment()");
            initDefaultDataSource(environment);
            initCustomDataSources(environment);
        }
    
        /**
         * 加载主数据源配置.
         */
        private void initDefaultDataSource(Environment env) {
            // 读取主数据源
            RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
            Map<String, Object> dsMap = new HashMap<String, Object>();
            dsMap.put("type", propertyResolver.getProperty("type"));
            dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
            dsMap.put("url", propertyResolver.getProperty("url"));
            dsMap.put("username", propertyResolver.getProperty("username"));
            dsMap.put("password", propertyResolver.getProperty("password"));
    
            //创建数据源;
            defaultDataSource = buildDataSource(dsMap);
            dataBinder(defaultDataSource, env);
        }
    
        /**
         * 初始化更多数据源
         */
        private void initCustomDataSources(Environment env) {
            // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
            RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
            String dsPrefixs = propertyResolver.getProperty("names");
            for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
                Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
                DataSource ds = buildDataSource(dsMap);
                customDataSources.put(dsPrefix, ds);
                dataBinder(ds, env);
            }
        }
    
        /**
         * 创建datasource.
         */
        @SuppressWarnings("unchecked")
        public DataSource buildDataSource(Map<String, Object> dsMap) {
            Object type = dsMap.get("type");
            if (type == null) {
                type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
            }
            Class<? extends DataSource> dataSourceType;
    
            try {
                dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
                String driverClassName = dsMap.get("driverClassName").toString();
                String url = dsMap.get("url").toString();
                String username = dsMap.get("username").toString();
                String password = dsMap.get("password").toString();
                DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dataSourceType);
                return factory.build();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 为DataSource绑定更多数据
         */
        private void dataBinder(DataSource dataSource, Environment env) {
            RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
            dataBinder.setConversionService(conversionService);
            dataBinder.setIgnoreNestedProperties(false);//false
            dataBinder.setIgnoreInvalidFields(false);//false
            dataBinder.setIgnoreUnknownFields(true);//true
    
            if (dataSourcePropertyValues == null) {
                Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
                Map<String, Object> values = new HashMap<>(rpr);
                // 排除已经设置的属性
                values.remove("type");
                values.remove("driverClassName");
                values.remove("url");
                values.remove("username");
                values.remove("password");
                dataSourcePropertyValues = new MutablePropertyValues(values);
            }
            dataBinder.bind(dataSourcePropertyValues);
        }
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()");
            Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
            // 将主数据源添加到更多数据源中
            targetDataSources.put("dataSource", defaultDataSource);
            DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
            // 添加更多数据源
            targetDataSources.putAll(customDataSources);
            for (String key : customDataSources.keySet()) {
                DynamicDataSourceContextHolder.dataSourceIds.add(key);
            }
    
            // 创建DynamicDataSource
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(DynamicDataSource.class);
    
            beanDefinition.setSynthetic(true);
            MutablePropertyValues mpv = beanDefinition.getPropertyValues();
            //添加属性:AbstractRoutingDataSource.defaultTargetDataSource
            mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
            mpv.addPropertyValue("targetDataSources", targetDataSources);
            registry.registerBeanDefinition("dataSource", beanDefinition);
        }
    
    }

     1 import java.lang.annotation.Documented;
     2 import java.lang.annotation.ElementType;
     3 import java.lang.annotation.Retention;
     4 import java.lang.annotation.RetentionPolicy;
     5 import java.lang.annotation.Target;
     6 
     7 /**
     8  * 在方法上使用,用于指定使用哪个数据源
     9   */
    10 @Target({ElementType.METHOD,ElementType.TYPE})
    11 @Retention(RetentionPolicy.RUNTIME)
    12 @Documented
    13 public @interface TargetDataSource {
    14     String value();
    15 }

    测试:

    在Controller里:

    1 @Resource
    2     private TestService testService;

     1 @Service
     2 public class TestService {
     3 
     4     @Resource
     5     private TestDao testDao;
     6     
     7 
     8     /**
     9      * 不指定数据源使用默认数据源
    10      * @return
    11      */
    12     public List<User> getList(){
    13         return testDao.getList();
    14     }
    15 
    16     /**
    17      * 指定数据源
    18      * @return
    19      */
    20     @TargetDataSource("ds1")
    21     public List<User> getListByDs1(){
    22         //return testDao.getListByDs1();
    23     }
    24 
    25 }

    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TestDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        /**
         * 不指定数据源使用默认数据源
         * @return
         */
        public List<User> getList(){
            String sql = "select * from user";
            return (List<User>) jdbcTemplate.query(sql, new RowMapper<User>(){
                @Override
                public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                    User user = new User();
                    user.setId(rs.getLong("id"));
                    user.setName(rs.getString("name"));;
                    return user;
                }
            });
        }
    
        /**
         * 指定数据源
         * 在对应的service进行指定;
         * @return
         * @author SHANHY
         * @create  2016年1月24日
         */
        public List<User> getListByDs1(){
            /*
             * 这张表示复制的主库到ds1的,在ds中并没有此表.
             * 需要自己自己进行复制,不然会报错:Table 'test1.User1' doesn't exist
             */
            String sql = "select * from user";
            return (List<User>) jdbcTemplate.query(sql, new RowMapper<User>(){
    
                @Override
                public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                    User user = new User();
                    user.setId(rs.getLong("id"));
                    user.setName(rs.getString("name"));;
                    return user;
                }
            });
        }
    }

    至于user表和user对应的实体类,自己创建一个就好。

  • 相关阅读:
    CentOS安装Nginx Pre-Built
    CMake设置编译参数
    SQLServer脚本编写
    使用QNetworkAccessManager实现Qt的FTP下载服务
    使用CMD命令设置IP
    IIS6(Win2003) 使用.net 4.0 后,默认文档失效解决方案。
    windows7打印时,显示脱机,提示“服务器打印后台处理程序服务没有运行”。
    阻止浏览器自动填表
    Java经典编程题50道之四
    Java经典编程题50道之三
  • 原文地址:https://www.cnblogs.com/wdpnodecodes/p/8027099.html
Copyright © 2020-2023  润新知