43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】

在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库。那么这一节我们将要介绍怎么进行多数据源动态切换。添加本文实现的代码之后,只需要配置要数据源就可以直接通过注解使用,在实际使用的时候特别的简单。那么本章主要分以下几个步骤进行实战。

(1)新建maven javaproject;

(2)在pom.xml添加依赖包;

(3)编写启动类App.java

(4)编写配置文件application.properties;

(5)动态数据源路由类;

(6)注册多数据源;

(7)测试类测试;

       接下来我们看看每一步具体的实现吧:

(1)新建maven java project;

       新建一个maven project,取名为:spring-boot-multi-ds

(2)在pom.xml添加依赖包;

       在pom.xml文件中加入依赖的库文件,主要是springboot基本的,数据库驱动,spring-jpa支持即可,具体pom.xml文件如下:

<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

 <modelVersion>4.0.0</modelVersion>

 

 <groupId>com.kfit</groupId>

 <artifactId>spring-boot-multids</artifactId>

 <version>0.0.1-SNAPSHOT</version>

 <packaging>jar</packaging>

 

 <name>spring-boot-multids</name>

 <url>http://maven.apache.org</url>

 

 <properties>

   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

   <!--jdk版本号,这里需要你本地进行的jdk进行修改,这里angel使用的是1.8的版本. -->

   <java.version>1.8</java.version>

 </properties>

 

 

  <!--

              springboot父节点依赖,

             引入这个之后相关的引入就不需要添加version配置,

              springboot会自动选择最合适的版本进行添加。

             在这里使用的1.3.3版本,可能目前官方有最新的版本了,大家可以

             使用最新的版本。

        -->

       <parent>

              <groupId>org.springframework.boot</groupId>

              <artifactId>spring-boot-starter-parent</artifactId>

              <version>1.3.3.RELEASE</version>

       </parent>

 

 <dependencies>

     <!-- 单元测试包,在这里没有使用到.-->

   <dependency>

     <groupId>junit</groupId>

     <artifactId>junit</artifactId>

     <scope>test</scope>

   </dependency>

   

   <!-- spring boot web支持:mvc,aop...

         这个是最基本的,基本每一个基本的demo都是需要引入的。

   -->

       <dependency>

              <groupId>org.springframework.boot</groupId>

              <artifactId>spring-boot-starter-web</artifactId>

       </dependency>

      

       <!-- mysql驱动.

             我们的demo是多数据源,在这里使用Mysql数据库.

       -->

       <dependency>

              <groupId>mysql</groupId>

              <artifactId>mysql-connector-java</artifactId>

       </dependency>

      

      

       <!-- springjpa

              springjpa中带有自带的tomcat 数据连接池;

             在代码中我们也需要用到.

        -->

       <dependency>

              <groupId>org.springframework.boot</groupId>

              <artifactId>spring-boot-starter-data-jpa</artifactId>

       </dependency>

   

 </dependencies>

</project>

       在上面的配置文件中都有相应的解释,大家可以自己解读下。

(3)编写启动类App.java

       编写spring boot的启动类:

com.kfit.App:

package com.kfit;

 

import org.springframework.boot.SpringApplication;

importorg.springframework.boot.autoconfigure.SpringBootApplication;

 

/**

 *

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@SpringBootApplication

publicclass App {

       publicstaticvoid main(String[]args) {

              SpringApplication.run(App.class,args);

       }

}

(4)编写配置文件application.properties;

       在这里主要是多数据源和jpa的配置:

src/main/resources/application.properties:

########################################################

###配置文件包括1个主数据源和多个数据源,

###其中主数据源在Spring中的beanName默认为dataSource,

###另外几个数据源的beanName分包为:ds1、ds2、ds3

###其中datasource的type属性可以具体指定到我们需要的数据源上面,

###不指定情况下默认为:org.apache.tomcat.jdbc.pool.DataSource

###当然你也可以把这些数据源配置到主dataSource数据库中,然后读取数据库生成多数据源。当然这样做的必要性并不大,难不成数据源还会经常变吗。

########################################################

 

#主数据源,默认的

#spring.datasource.type=com.zaxxer.hikari.HikariDataSource

spring.datasource.driverClassName=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/test

spring.datasource.username=root

spring.datasource.password=root

 

 

#更多数据源

custom.datasource.names=ds1,ds2,ds3

#custom.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource

custom.datasource.ds1.driverClassName=com.mysql.jdbc.Driver

custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1

custom.datasource.ds1.username=root

custom.datasource.ds1.password=root

 

#custom.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource

custom.datasource.ds2.driverClassName=com.mysql.jdbc.Driver

custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test

custom.datasource.ds2.username=root

custom.datasource.ds2.password=root

 

#custom.datasource.ds3.type=com.zaxxer.hikari.HikariDataSource

custom.datasource.ds3.driverClassName=com.mysql.jdbc.Driver

custom.datasource.ds3.url=jdbc:mysql://localhost:3306/test

custom.datasource.ds3.username=root

custom.datasource.ds3.password=root

 

 

#下面为连接池的补充设置,应用到上面所有数据源中

spring.datasource.maximum-pool-size=100

spring.datasource.max-idle=10

spring.datasource.max-wait=10000

spring.datasource.min-idle=5

spring.datasource.initial-size=5

spring.datasource.validation-query=SELECT 1

spring.datasource.test-on-borrow=false

spring.datasource.test-while-idle=true

spring.datasource.time-between-eviction-runs-millis=18800

 

 

 

########################################################

### Java Persistence Api

########################################################

# Specify the DBMS

spring.jpa.database= MYSQL

# Show or not log for each sqlquery

spring.jpa.show-sql= true

# Hibernate ddl auto (create,create-drop, update)

spring.jpa.hibernate.ddl-auto= update

# Naming strategy

#[org.hibernate.cfg.ImprovedNamingStrategy  #org.hibernate.cfg.DefaultNamingStrategy]

spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultNamingStrategy

# stripped before adding them tothe entity manager)

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

 

(5)动态数据源路由类;

       动态数据源能进行自动切换的核心就是spring底层提供了AbstractRoutingDataSource类进行数据源的路由的,我们主要继承这个类,实现里面的方法即可实现我们想要的,这里主要是实现方法:determineCurrentLookupKey(),而此方法只需要返回一个数据库的名称即可,所以我们核心的是有一个类来管理数据源的线程池,这个类才是动态数据源的核心处理类。还有另外就是我们使用aop技术在执行事务方法前进行数据源的切换。所以这里有几个需要编码的类,具体如下:

 

动态数据源上下文>com.kfit.config.datasource.DynamicDataSourceContextHolder:

package com.kfit.config.datasource.dynamic;

 

import java.util.ArrayList;

import java.util.List;

 

/**

 *动态数据源上下文.

 *

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

publicclass DynamicDataSourceContextHolder {

      

       /*

        *当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,

        *所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

        */

       privatestaticfinalThreadLocal<String>contextHolder =new ThreadLocal<String>();

       /*

        *管理所有的数据源id;

        *主要是为了判断数据源是否存在;

        */

       publicstaticList<String>dataSourceIds =new ArrayList<String>();

 

       /**

        *使用setDataSourceType设置当前的

        *@param dataSourceType

        */

       publicstaticvoidsetDataSourceType(StringdataSourceType) {

              contextHolder.set(dataSourceType);

       }

 

       publicstatic StringgetDataSourceType() {

              returncontextHolder.get();

       }

 

       publicstaticvoidclearDataSourceType() {

              contextHolder.remove();

       }

      

       /**

    *判断指定DataSrouce当前是否存在

    *

    *@param dataSourceId

    *@return

    *@author SHANHY

    *@create  2016年1月24日

    */

   publicstaticboolean containsDataSource(StringdataSourceId){

        returndataSourceIds.contains(dataSourceId);

   }

}

数据源路由类>com.kfit.config.datasource.dynamic.DynamicDataSource:

package com.kfit.config.datasource.dynamic;

 

importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

 

/**

 *动态数据源.

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

publicclass DynamicDataSourceextendsAbstractRoutingDataSource {

      

       /*

        *代码中的determineCurrentLookupKey方法取得一个字符串,

        *该字符串将与配置文件中的相应字符串进行匹配以定位数据源,配置文件,即applicationContext.xml文件中需要要如下代码:(non-Javadoc)

        * @seeorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()

        */

       @Override

       protected ObjectdetermineCurrentLookupKey() {

              /*

               * DynamicDataSourceContextHolder代码中使用setDataSourceType

               *设置当前的数据源,在路由类中使用getDataSourceType进行获取,

               * 交给AbstractRoutingDataSource进行注入使用。

               */

              returnDynamicDataSourceContextHolder.getDataSourceType();

       }

}

指定数据源注解类>com.kfit.config.datasource.dynamic.TargetDataSource:

package com.kfit.config.datasource.dynamic;

 

importjava.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

importjava.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

/**

 *在方法上使用,用于指定使用哪个数据源

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@Target({ ElementType.METHOD, ElementType.TYPE })

@Retention(RetentionPolicy.RUNTIME)

@Documented

public@interfaceTargetDataSource {

       Stringvalue();

}

切换数据源Advice>com.kfit.config.datasource.dynamic.DynamicDataSourceAspect:

package com.kfit.config.datasource.dynamic;

 

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

importorg.springframework.core.annotation.Order;

importorg.springframework.stereotype.Component;

 

/**

 *切换数据源Advice

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@Aspect

@Order(-10)//保证该AOP在@Transactional之前执行

@Component

publicclass DynamicDataSourceAspect {

      

       /*

        * @Before("@annotation(ds)")

        *的意思是:

        *

        * @Before:在方法执行之前进行执行:

        * @annotation(targetDataSource)

        *会拦截注解targetDataSource的方法,否则不拦截;

        */

       @Before("@annotation(targetDataSource)")

   publicvoid changeDataSource(JoinPointpoint, TargetDataSourcetargetDataSource) throws Throwable {

              //获取当前的指定的数据源;

        String dsId = targetDataSource.value();

        //如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。

        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {

            System.err.println("数据源[{}]不存在,使用默认数据源 > {}"+targetDataSource.value()+point.getSignature());

        } else {

               System.out.println("UseDataSource : {} > {}"+targetDataSource.value()+point.getSignature());

               //找到的话,那么设置到动态数据源上下文中。

            DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());

        }

   }

      

       @After("@annotation(targetDataSource)")

   publicvoid restoreDataSource(JoinPointpoint, TargetDataSourcetargetDataSource) {

              System.out.println("RevertDataSource : {} > {}"+targetDataSource.value()+point.getSignature());

              //方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。

        DynamicDataSourceContextHolder.clearDataSourceType();

   }

      

}






(6)注册多数据源;

       以上都是动态数据源在注入的时候使用的代码,其实很重要的一部分代码就是注册我们在application.properties配置的多数据源,这才是重点,这里我们使用

ImportBeanDefinitionRegistrar进行注册,具体的代码如下:

package com.kfit.config.datasource.dynamic;

 

import java.util.HashMap;

import java.util.Map;

 

import javax.sql.DataSource;

 

importorg.springframework.beans.MutablePropertyValues;

import org.springframework.beans.PropertyValues;

importorg.springframework.beans.factory.support.BeanDefinitionRegistry;

importorg.springframework.beans.factory.support.GenericBeanDefinition;

importorg.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;

importorg.springframework.boot.bind.RelaxedDataBinder;

importorg.springframework.boot.bind.RelaxedPropertyResolver;

importorg.springframework.context.EnvironmentAware;

importorg.springframework.context.annotation.ImportBeanDefinitionRegistrar;

import org.springframework.core.convert.ConversionService;

importorg.springframework.core.convert.support.DefaultConversionService;

importorg.springframework.core.env.Environment;

importorg.springframework.core.type.AnnotationMetadata;

 

/**

 *动态数据源注册;

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

publicclass DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

      

       //如配置文件中未指定数据源类型,使用该默认值

   privatestaticfinal ObjectDATASOURCE_TYPE_DEFAULT ="org.apache.tomcat.jdbc.pool.DataSource";

   private ConversionServiceconversionService =newDefaultConversionService();

   private PropertyValuesdataSourcePropertyValues;

   

       // 默认数据源

   private DataSourcedefaultDataSource;

      

   private Map<String, DataSource>customDataSources =newHashMap<String, DataSource>();

   

       /**

        *加载多数据源配置

        */

       @Override

       publicvoidsetEnvironment(Environmentenvironment) {

              System.out.println("DynamicDataSourceRegister.setEnvironment()");

              initDefaultDataSource(environment);

        initCustomDataSources(environment);

       }

      

       /**

        *加载主数据源配置.

        *@param env

        */

       privatevoidinitDefaultDataSource(Environmentenv){

              // 读取主数据源

              RelaxedPropertyResolverpropertyResolver =newRelaxedPropertyResolver(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);

       }

      

       /**

    *初始化更多数据源

    *

    *@author SHANHY

    *@create 2016年1月24日

    */

   privatevoid initCustomDataSources(Environmentenv) {

       //读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源

        RelaxedPropertyResolverpropertyResolver =newRelaxedPropertyResolver(env,"custom.datasource.");

        String dsPrefixs = propertyResolver.getProperty("names");

        for (StringdsPrefix :dsPrefixs.split(",")) {//多个数据源

            Map<String, Object>dsMap =propertyResolver.getSubProperties(dsPrefix +".");

            DataSource ds = buildDataSource(dsMap);

            customDataSources.put(dsPrefix, ds);

            dataBinder(ds,env);

        }

   }

      

       /**

        *创建datasource.

        *@param dsMap

        *@return

        */

       @SuppressWarnings("unchecked")

       public DataSourcebuildDataSource(Map<String, Object>dsMap) {

              Objecttype =dsMap.get("type");

        if (type ==null){

            type = DATASOURCE_TYPE_DEFAULT;//默认DataSource

        }

        Class<? extends DataSource>dataSourceType;

       

              try {

                     dataSourceType = (Class<? extends DataSource>)Class.forName((String)type);

                     StringdriverClassName =dsMap.get("driverClassName").toString();

               String url = dsMap.get("url").toString();

               String username = dsMap.get("username").toString();

               String password = dsMap.get("password").toString();

               DataSourceBuilderfactory =   DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dataSourceType);

               returnfactory.build();

              }catch(ClassNotFoundExceptione) {

                     e.printStackTrace();

              }

              returnnull;

       }

      

       /**

        *为DataSource绑定更多数据

        *@param dataSource

        *@param env

        */

       privatevoiddataBinder(DataSourcedataSource, Environment env){

              RelaxedDataBinderdataBinder =newRelaxedDataBinder(dataSource);

              dataBinder.setConversionService(conversionService);

              dataBinder.setIgnoreNestedProperties(false);//false

        dataBinder.setIgnoreInvalidFields(false);//false

        dataBinder.setIgnoreUnknownFields(true);//true

       

        if(dataSourcePropertyValues ==null){

            Map<String, Object>rpr =newRelaxedPropertyResolver(env,"spring.datasource").getSubProperties(".");

            Map<String, Object>values =newHashMap<>(rpr);

            // 排除已经设置的属性

            values.remove("type");

            values.remove("driverClassName");

            values.remove("url");

            values.remove("username");

            values.remove("password");

            dataSourcePropertyValues = new MutablePropertyValues(values);

        }

        dataBinder.bind(dataSourcePropertyValues);

       

       }

      

 

       @Override

       publicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry) {

              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 (Stringkey :customDataSources.keySet()) {

            DynamicDataSourceContextHolder.dataSourceIds.add(key);

        }

       

        // 创建DynamicDataSource

        GenericBeanDefinitionbeanDefinition =newGenericBeanDefinition();

        beanDefinition.setBeanClass(DynamicDataSource.class);

       

        beanDefinition.setSynthetic(true);

        MutablePropertyValuesmpv =beanDefinition.getPropertyValues();

        //添加属性:AbstractRoutingDataSource.defaultTargetDataSource

        mpv.addPropertyValue("defaultTargetDataSource",defaultDataSource);

        mpv.addPropertyValue("targetDataSources",targetDataSources);

        registry.registerBeanDefinition("dataSource",beanDefinition);

       }

 

}

这里还有一个步骤很重要,由于我们是使用的ImportBeanDefinitionRegistrar的方式进行注册的,所以我们需要在App.java类中使用@Import进行注册,具体改造之后的App.java代码如下:

package com.kfit;

 

importorg.springframework.boot.SpringApplication;

importorg.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Import;

importcom.kfit.config.datasource.dynamic.DynamicDataSourceRegister;

 

/**

 *

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@SpringBootApplication

//注册动态多数据源

@Import({DynamicDataSourceRegister.class})

publicclass App {

       publicstaticvoid main(String[]args) {

              SpringApplication.run(App.class,args);

       }

}

(7)测试类测试;

       核心的代码都编写完毕了,当然我们肯定是需要编写代码进行测试下,我们才放心了。

com.kfit.demo.bean.Demo:

package com.kfit.demo.bean;

 

import javax.persistence.Entity;

importjavax.persistence.GeneratedValue;

import javax.persistence.Id;

 

/**

 *测试demo类.

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@Entity

publicclass Demo {

       @Id @GeneratedValue

       privatelongid;

      

       private Stringname;

 

       publiclong getId() {

              returnid;

       }

 

       publicvoid setId(longid) {

              this.id =id;

       }

 

       public StringgetName() {

              returnname;

       }

 

       publicvoid setName(Stringname) {

              this.name =name;

       }

 

       @Override

       public StringtoString() {

              return"Demo[id=" +id +", name=" +name +"]";

       }

}

com.kfit.demo.dao.TestDao:

package com.kfit.demo.dao;

 

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.List;

 

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.jdbc.core.JdbcTemplate;

importorg.springframework.jdbc.core.RowMapper;

import org.springframework.stereotype.Service;

 

import com.kfit.demo.bean.Demo;

 

@Service

publicclass TestDao {

      

       @Autowired

   private JdbcTemplatejdbcTemplate;

      

       /**

        *不指定数据源使用默认数据源

        *@return

        */

       publicList<Demo> getList(){

              Stringsql ="select*from Demo";

        return (List<Demo>)jdbcTemplate.query(sql,newRowMapper<Demo>(){

            @Override

            public Demo mapRow(ResultSetrs,introwNum)throws SQLException {

                   Demodemo =new Demo();

                   demo.setId(rs.getLong("id"));

                   demo.setName(rs.getString("name"));;

                returndemo;

            }

        });

       }

      

        /**

    *指定数据源

    *在对应的service进行指定;

    *@return

    *@author SHANHY

    *@create  2016年1月24日

    */

   public List<Demo> getListByDs1(){

          /*

           *这张表示复制的主库到ds1的,在ds中并没有此表.

           *需要自己自己进行复制,不然会报错:Table 'test1.demo1'doesn't exist

           */

              Stringsql ="select*from Demo1";

        return(List<Demo>)jdbcTemplate.query(sql,new RowMapper<Demo>(){

 

            @Override

            publicDemo mapRow(ResultSetrs, introwNum) throws SQLException {

                   Demodemo =new Demo();

                   demo.setId(rs.getLong("id"));

                   demo.setName(rs.getString("name"));;

                returndemo;

            }

 

        });

   }

}

com.kfit.demo.service.TestService :

package com.kfit.demo.service;

 

import java.util.List;

 

import javax.annotation.Resource;

 

importorg.springframework.stereotype.Service;

 

importcom.kfit.config.datasource.dynamic.TargetDataSource;

import com.kfit.demo.bean.Demo;

import com.kfit.demo.dao.TestDao;

 

@Service

publicclass TestService {

      

       @Resource

       private TestDaotestDao;

      

       /**

        *不指定数据源使用默认数据源

        *@return

        */

       publicList<Demo> getList(){

              returntestDao.getList();

       }

      

       /**

        *指定数据源

        *@return

        */

   @TargetDataSource("ds1")

   public List<Demo> getListByDs1(){

          returntestDao.getListByDs1();

   }

}

com.kfit.demo.controller.TestController:

package com.kfit.demo.controller;

 

import javax.annotation.Resource;

 

importorg.springframework.web.bind.annotation.RequestMapping;

importorg.springframework.web.bind.annotation.RestController;

 

import com.kfit.demo.bean.Demo;

import com.kfit.demo.service.TestService;

 

@RestController

publicclass TestController {

      

       @Resource

       private TestServicetestService;

      

       @RequestMapping("/test1")

       public String test(){

//           for(Demod:testService.getList()){

//                  System.out.println(d);

//           }

              for(Demod:testService.getListByDs1()){

                     System.out.println(d);

              }

              return"ok";

       }

      

}

 

好了,测试代码就这么多了,运行App.java进行测试把,访问:

http://127.0.0.1:8080/test1查看控制台的打印。

这里需要提醒下,这种方式spring-jpa的方式好像不能自动路由,博主打算在之后的一篇文章介绍spring-jpa多数据源的问题。 


购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287