SpringBoot集成实现多数据

在有的项目开发中需要在一个项目中访问多个数据源或者两个项目之间通信(实质上是互相访问对方的数据库),在这里,我们介绍一下在一个项目中如何集成多个数据源(即访问多个不同的数据库),因为在项目中有时会有这种需求,比如在一个大型项目开发中,一个数据库中保存数据的索引,各种使用频繁的数据,另一个数据库中保存其他的数据。

1.下面我们来讨论一个问题,怎么集成多数据源,就是怎么让一个项目访问多个数据库?
有的人会说使用注解,没错,这是一种办法,因为springboot对的最大好处就是避免了繁琐的xml配置文件,大量的使用注解来开发,方便简洁,但是在这里如果集成多数据源使用注解的话会很麻烦,有没有其他的办法呢?答案是肯定的,我们可以分模板来访问多个数据库,也就是分包。

2.如何分包来访问多个数据源?
在这里,我们用一个简单的案例来说明,我们访问新建一个spingboot项目,访问test1,test2这两个数据库,首先,我们先看代码。

首先,我们需要导入相关依赖在pom文件中,这里,因为我的项目已经提前有了父pom,所以不再考虑依赖的版本问题,怎么建立父pom可参考上一篇文章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  <parent>
    <groupid>cn.shinelon.springboot</groupid>
    microboot</artifactid>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  springboot-MultiDatasources</artifactid>
  <packaging>war</packaging>
 
 
  <!-- maven项目packaging改为war类型时,必须要加这个插件 -->
    <build>
        <plugins>
            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                maven-war-plugin</artifactid>
                <version>2.3</version>
                <configuration>
                    <failonmissingwebxml>false</failonmissingwebxml>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            spring-boot-starter-web</artifactid>
        </dependency>
        <!-- 测试springboot的依赖 -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
 
    <!-- 测试时加入这两个依赖,当修改代码的时候就不用每次修改后重启服务器了 -->
    <dependency>
        <groupid>org.springframework</groupid>
        springloaded</artifactid>
    </dependency>
    <dependency>
        <groupid>org.springframework.boot</groupid>
        spring-boot-devtools</artifactid>
    </dependency>
    <!-- springboot 集成jsp必须要借助这两个依赖 -->
    <dependency>
        <groupid>org.springframework.boot</groupid>
        spring-boot-starter-tomcat</artifactid>
    </dependency>
    <dependency>
        <groupid>org.apache.tomcat.embed</groupid>
        tomcat-embed-jasper</artifactid>
    </dependency>
    <!-- springboot集成mybatis -->
    <dependency>
        <groupid>org.mybatis.spring.boot</groupid>
        mybatis-spring-boot-starter</artifactid>
        <version>1.3.0</version>
    </dependency>
    <dependency>
        <groupid>mysql</groupid>
        mysql-connector-java</artifactid>
    </dependency>
  </dependencies>
</project>

在上面配置的pom文件中,有点要说明,因为要访问数据库,所以我整合了mybatis,还有一个是整合jsp,不过在这个项目中无关,是我之前写代码留下来的,可不必关心。

下面,我们还要在scr/main/sources目录下新建一个application.properties资源文件,注意,这个文件名必须是application,这个是固定的,springboot默认访问该文件,不要自己乱改名称。然后在这个资源文件中配置自定义数据源。

1
2
3
4
5
6
7
8
9
10
spring.datasource.test1.driverClassName=com.mysql.jdbc.Driver
spring.datasource.test1.url=jdbc:mysql://localhost:3306/test1
spring.datasource.test1.username=root
spring.datasource.test1.password=.....
 
 
spring.datasource.test2.driverClassName=com.mysql.jdbc.Driver
spring.datasource.test2.url=jdbc:mysql://localhost:3306/test2
spring.datasource.test2.username=root
spring.datasource.test2.password=.....

配置好数据源后我们就可以开进行分模块来访问这两个数据源了。首先在src/mian/java目录下创建好各个包,然后开始开发,新建一个datasource包来放置需要访问的两个数据源的代码,然后在新建两个模块包test1和test2,从包名我们就可以看出来这两个包是用来操作这两个数据库,在这两个包下可以分别建立DAO层和service层的包,然后建立一个controller控制层。下面是项目的目录树图。

SpringBoot集成实现多数据

然后我们来写DataSource中的代码,其实就是像我们以前在xml文件中配置的SqlSessionFactory和数据源一个原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import javax.sql.DataSource;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
@Configuration
@MapperScan(basePackages="cn.shinelon.test1",sqlSessionFactoryRef="test1SqlSessionFactory")
public class Datasource1 {
    /**
     * 配置test1数据库
     * @return
     */
    @Bean(name="test1Datasource")
    @ConfigurationProperties(prefix="spring.datasource.test1")
    public DataSource testDatasource() {
        return DataSourceBuilder.create().build();
    }
    /**
     * 创建SqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name="test1SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1Datasource")DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //如果还有分页等其他事务
//      bean.setMapperLocations(new PathMatchingResourcePatternResolver().
//              getResources("classpath:mybatis/test1/*.xml"));
        return bean.getObject();
    }
    /**
     * 配置事务管理
     * @param dataSource
     * @return
     */
    @Bean(name="test1TransactionManager")
    public DataSourceTransactionManager testTransactionManager(
            @Qualifier("test1Datasource")DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
 
    @Bean(name="test1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory")
    SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

上面的是访问的test1数据库的配置,需要注意的是@ConfigurationProperties(prefix=”spring.datasource.test1”)这个配置中的属性prefix的值必须和资源文件中的前缀是一样的,否则是访问不到的,还有@Primary ,如果不加这个注解,启动将会报错,因为服务器先要有一个默认不知道默认要先访问的数据源是哪个,必须指明,下面是test2数据库的配置,和上面一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import javax.sql.DataSource;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
@Configuration
@MapperScan(basePackages="cn.shinelon.test2",sqlSessionFactoryRef="test2SqlSessionFactory")
@Primary        //指定 默认的访问的数据源
public class Datasource2 {
    /**
     * 配置test2数据库
     * @return
     */
    @Bean(name="test2Datasource")
    @ConfigurationProperties(prefix="spring.datasource.test2")
    @Primary        //指定 默认的访问的数据源
    public DataSource testDatasource() {
        return DataSourceBuilder.create().build();
    }
    /**
     * 创建SqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name="test2SqlSessionFactory")
    @Primary        //指定 默认的访问的数据源
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2Datasource")DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //如果还有分页等其他事务
//      bean.setMapperLocations(new PathMatchingResourcePatternResolver().
//              getResources("classpath:mybatis/test2/*.xml"));
        return bean.getObject();
    }
    /**
     * 配置事务管理
     * @param dataSource
     * @return
     */
    @Bean(name="test2TransactionManager")
    @Primary        //指定 默认的访问的数据源
    public DataSourceTransactionManager testTransactionManager(
            @Qualifier("test2Datasource")DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
 
    @Bean(name="test2SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test2SqlSessionFactory")
    SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

然后我们在每一个模块中写DAO层和service代码来进行操作。
DAO层代码如下

1
2
3
4
public interface User1Dao {
    @Insert("insert into user values(null,#{username},#{age})")
    public void insert(@Param("username")String username,@Param("age")int age);
}

在这里,我们向数据库插入一条数据。
service层代码如下

1
2
3
4
5
6
7
8
9
@Service
public class User1Service {
    @Autowired
    public User1Dao user1Dao;
 
    public void insert(String username,int age) {
        user1Dao.insert(username, age);
    }
}

test2包下DAO层和service层的代码都是同样的,这里就省略了。
然后我们来开始写controller层的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import cn.shinelon.test1.services.User1Service;
import cn.shinelon.test2.services.User2Service;
 
@SpringBootApplication
@ComponentScan(basePackages={"cn.shinelon.datasource","cn.shinelon.test1","cn.shinelon.test2"})
@RestController
public class UserController {
    @Autowired
    public User1Service user1Service;
    @Autowired
    public User2Service user2Service;
    @RequestMapping("/add")
    public String insert(String username,int age) {
        user1Service.insert(username, age);
        user2Service.insert(username, age);
        return "insert success";
    }
 
    public static void main(String[] args) {
        SpringApplication.run(UserController.class, args);
    }
}

在上面代码中 ,需要注意的是@ComponentScan(basePackages={“cn.shinelon.datasource”,”cn.shinelon.test1”,”cn.shinelon.test2”})这个注解,必须添加,这样服务器才会扫描到这几个包中的配置。

下面我们补全代码,还要建立一个实体类

1
2
3
4
5
6
public class User {
    private int id;
    private String username;
    private int age;
    //省略get,set方法
    }