SpringBoot配置多數據庫的數據源

微服務下項目絕對不是傳統的單體數據庫設計,此時就涉及到了多數據源

項目案例代碼上傳到git

https://gitee.com/gangye/springboot_more_databases

首先創建一個SpringBoot項目,具體項目結構

引入maven依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gangye</groupId>
    <artifactId>springboot_much_database</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <dependencies>
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

在application.properties文件中配置數據源的配置,以及mybatis的配置

server.port=8089

# master database
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/master_test_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=ok

# slave database
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/slave_test_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=ok

#mybatis的相關配置
#mybatis.mapper-locations=classpath:mapper/master/*.xml,classpath:mapper/slave/*.xml
mybatis.config-location=classpath:mybatis-config.xml

其中將url改成jdbc-url,不改的話啓動後會連接數據庫報錯java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.

有三種方法可以解決這個錯誤:

具體參考:https://blog.csdn.net/MrLi_IT/article/details/80909078

由於使用了多數據源,不是單一數據源,所以就不能使用默認的框架內的配置,需要自己寫配置類,當有一個數據庫時,創建一個數據源配置

package com.gangye.dataSourceConfig;

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.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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Classname DataSourceMasterConfig
 * @Description 數據庫爲master_test_database的數據源配置類,由於必須有一個作爲主庫,所以主庫時必須加上@Primary註解
 * @Date 2020/5/26 16:54
 * @Created by gangye
 */
@Configuration
@MapperScan(basePackages = "com.gangye.mapper.mapper1",sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class DataSourceMasterConfig {

    //mapper掃描xml文件的路徑
    static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";

    /**
     * 配置數據源
     * primary是設置優先,因爲有多個數據源,在沒有明確指定用哪個的情況下,會用帶有primary的,這個註解必須有一個數據源要添加
     */
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @Primary
    public DataSource masterDataSource(){
        return DataSourceBuilder.create().build();
    }

    /**
     * 配置SqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name = "primarySqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception{
        final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * 配置事務管理器
     * @param dataSource
     * @return
     */
    @Bean(name = "primaryTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 結合類註解MapperScan配置MapperScannerConfigurer
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "primarySqlSessionTemplate")
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

爲第二個數據庫創建配置類

package com.gangye.dataSourceConfig;

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.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Classname DataSourceSecondConfig
 * @Description 數據庫爲slave_test_database的數據源配置類,其餘的數據庫類似,非主數據庫不需加上@Primary註解
 * @Date 2020/5/27 8:44
 * @Created by gangye
 */
@Configuration
@MapperScan(basePackages = "com.gangye.mapper.mapper2",sqlSessionTemplateRef = "secondSqlSessionTemplate")
public class DataSourceSecondConfig {
    //mapper掃描xml文件的路徑
    final static String SECOND_DATASOURCE_MAPPER_LOCATION = "classpath:mapper/slave/*.xml";

    /**
     * 配置數據源
     * @return
     */
    @Bean(name = "secondDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource secondDataSource(){
        return DataSourceBuilder.create().build();
    }

    /**
     * 配置SqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name = "secondSqlSessionFactory")
    public SqlSessionFactory secondSqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception{
        final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SECOND_DATASOURCE_MAPPER_LOCATION));
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * 配置事務管理器
     * @param dataSource
     * @return
     */
    @Bean(name = "secondTransactionManager")
    public DataSourceTransactionManager secondTransactionManager(@Qualifier("secondDataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 結合類註解MapperScan配置MapperScannerConfigurer
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "secondSqlSessionTemplate")
    public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

後續工作類似單數據源的操作,創建實體類,mapper,service以及controller,在使用事務的時候對應的事務使用對應的註解即可

創建實體類User

package com.gangye.entity;

import lombok.Data;

/**
 * @Classname User
 * @Description 對應master_test_database的user表的實體類
 * @Date 2020/5/26 16:33
 * @Created by gangye
 */
@Data
public class User {
    private Integer id;
    private String name;
    private String description;
}

user的mapper層,亦或者dao層

package com.gangye.mapper.mapper1;

import com.gangye.entity.User;

import java.util.List;

/**
 * @Classname UserMapper
 * @Description user的dao層
 * @Date 2020/5/26 16:40
 * @Created by gangye
 */
public interface UserMapper {
    User findById(Integer id);
    List<User> showAll();
    void insert(User user);
    void update(User user);
}

對應的mapper.xml的文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.gangye.mapper.mapper1.UserMapper">

    <resultMap id="BaseResultMap" type="com.gangye.entity.User">
        <result column="id" property="id" />
        <result column="name" property="name" />
        <result column="description" property="description" />
    </resultMap>

    <sql id="Base_Column_List">
        id, name, description
    </sql>

    <select id="findById" resultMap="BaseResultMap" parameterType="java.lang.Integer">
        select <include refid="Base_Column_List"/> from user where id = #{id}
    </select>

    <select id="showAll" resultMap="BaseResultMap">
        select <include refid="Base_Column_List"/> from user
    </select>

    <insert id="insert" parameterType="com.gangye.entity.User">
        insert into user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">
                id,
            </if>
            <if test="name != null">
                name,
            </if>
            <if test="description !=null">
                description,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">
                #{id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="description != null">
                #{description,jdbcType=VARCHAR},
            </if>
        </trim>
    </insert>

    <update id="update" parameterType="com.gangye.entity.User" >
        update user
        <set>
            <if test="name != null and name != ''">name = #{name},</if>
            <if test="description != null and description != ''">description = #{description},</if>
        </set>
        where id = #{id}
    </update>
</mapper>

user的相關service層(省略了UserService接口),此處可以看到對應的事務註解使用的是@Transactional(transactionManager = "primaryTransactionManager")

package com.gangye.service.impl;

import com.gangye.entity.User;
import com.gangye.mapper.mapper1.UserMapper;
import com.gangye.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @Classname UserServiceImpl
 * @Description userSercice的接口實現類
 * @Date 2020/5/27 9:49
 * @Created by gangye
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserByPrimarykey(Integer id) {
        return userMapper.findById(id);
    }

    @Override
    public List<User> showAllUsers() {
        return userMapper.showAll();
    }

    @Override
    @Transactional(transactionManager = "primaryTransactionManager")
    public void insertOneUser(User user) {
        userMapper.insert(user);
    }

    @Override
    @Transactional(transactionManager = "primaryTransactionManager")
    public void updateUserInfo(User user) {
        userMapper.update(user);
    }
}

最後控制路由

package com.gangye.controller;

import com.gangye.entity.User;
import com.gangye.service.UserService;
import com.gangye.tools.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Classname UserController
 * @Description user相關的控制路由
 * @Date 2020/5/27 9:51
 * @Created by gangye
 */
@RestController
@RequestMapping(value = "/userController")
public class UserController {
    @Autowired
    private UserService userService;

    //展示所有用戶
    @PostMapping("/showAllUsers")
    public Response showAllUsers(){
        List<User> userList = userService.showAllUsers();
        Response response = Response.newResponse();
        return response.setData(userList);
    }

    //根據id查詢用戶信息
    @PostMapping("/showUserInfo")
    public Response showUserInfo(Integer id){
        Response response = Response.newResponse();
        User userInfo = userService.getUserByPrimarykey(id);
        return response.setData(userInfo);
    }

    //更改用戶信息
    @PostMapping("/updateUserInfo")
    public Response updateUserInfo(@RequestBody User user){
        Response response = Response.newResponse();
        if (user.getId()!=null && ! "".equals(user.getId())){
            User tempUser = userService.getUserByPrimarykey(user.getId());
            if (tempUser==null){
                return response.setCodeAndMessage(9999,"用戶不存在!");
            }
            userService.updateUserInfo(user);
            return response.OK();
        }
        return response.setCodeAndMessage(9999,"更改信息失敗!");
    }

    //新增用戶
    @PostMapping("/addUser")
    public Response addUser(@RequestBody User userInfo){
        Response response = Response.newResponse();
        userService.insertOneUser(userInfo);
        return response.OK();
    }
}

啓動項目,結合postman以及日誌查看:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章