Spring Boot 配合 Mybatis 實現多數據源

1 環境

plugins {
    id 'org.springframework.boot' version '2.3.5.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3'
    runtimeOnly 'mysql:mysql-connector-java'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

    // https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter
    compile group: 'com.alibaba', name: 'druid-spring-boot-starter', version: '1.2.3'
}

test {
    useJUnitPlatform()
}

2 Spring Boot 讀取 yml

2.1 yml 文件注意事項

  • 可在小寫字母前增加-_表示其大寫形式,如 userNameuser-nameuser_name 含義是一樣的
  • 字符串默認不用加上單引號或者雙引號
  • 值使用雙引號不會轉義字符串裏面的特殊字符,例如 name: "zhangsan \n lisi",對應的結果會換行
  • 值使用單引號會轉義特殊字符,特殊字符最終只是一個普通的字符串,例如 name: ‘zhangsan \n lisi’ 對應的結果爲 zhangsan \n lisi

2.2 @Value 註解

讀取第一個數據源的 url

@Value("${spring.datasource.first.url}")
private String url;

如果是靜態變量,無法使用上述方式注入,需要:

  • 所在類添加 @Component 註解
  • 定義一個任意名稱和任意參數名的方法,如下
private static String url;

@Value("${spring.datasource.first.url}")
public void setUrl(String param){
    url=param;
}

2.3 @ConfigurationProperties 註解

需要一個 JavaBean 來專門映射配置,精簡案例如下,以此類推:

@Component
@ConfigurationProperties(prefix = "spring.datasource.first")
public class Student {

    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

注:prefix 僅描述到配置參數的上一級

2.4 @Environment 註解

@Autowired
private Environment env

public String test() {
    System.out.println(env.getProperty("spring.datasource.first.url"));
}

3 案例項目

3.1 主要項目結構

├── src/
│   └── main/
│       ├── java/
│       │   ├── config/
│       │   │   ├── FirstDataSourceConfig.java        # 第一個數據源配置
│       │   │   └── SecondDataSourceConfig.java       # 第二個數據源配置
│       │   ├── action/
│       │   │   └── Controller.java                   # 請求處理
│       │   ├── dao/
│       │   │   ├── first/
│       │   │   │   └── FirstDataSourceMapping.java
│       │   │   └── second/
│       │   │       └── SecondDataSourceConfig.java
│       │   ├── service/
│       │   │   └── DataService.java
│       │   └── Application.java                      # 應用入口
│       └── resources/
│           ├── mapper/
│           │   ├── FirstDataSourceMapping.xml
│           │   └── SecondDataSourceMapping.xml
│           └── application.yml
└── (其他)

application.yml

spring:
  profiles:
    active: product
  datasource:
    first:
      driverClassName: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/onlytest
      username: root
      password: 123456
    second:
      driverClassName: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/onlytest
      username: temp
      password: temp

server:
  port: 3005

mybatis:
  configuration:
    call-setters-on-nulls: true

Application.java

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {

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

config/FirstDataSourceConfig.java

@Configuration
@MapperScan(basePackages = {"com.dao.first"}, sqlSessionFactoryRef = "firstSqlSessionFactory")
public class FirstDataSourceConfig {

    @Value("${spring.datasource.first.url}")
    private String url;

    @Value("${spring.datasource.first.username}")
    private String user;

    @Value("${spring.datasource.first.password}")
    private String password;

    @Value("${spring.datasource.first.driverClassName}")
    private String driverClassName;

    @Bean(name = "firstDataSource")
    @Primary
    public DataSource dataSource() {
        DataSourceBuilder dataSourceBuilder=DataSourceBuilder.create();
        dataSourceBuilder.driverClassName(driverClassName);
        dataSourceBuilder.url(url);
        dataSourceBuilder.password(password);
        dataSourceBuilder.username(user);
        return dataSourceBuilder.build();
    }

    @Bean(name = "firstTransactionManager")
    @Primary
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean(name = "firstSqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("firstDataSource") DataSource masterDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/FirstDataSourceMapping.xml"));
        return sessionFactory.getObject();
    }
}

config/SecondDataSourceConfig.java

@Configuration
@MapperScan(basePackages = {"com.dao.second"}, sqlSessionFactoryRef = "secondSqlSessionFactory")
public class SecondDataSourceConfig {

    @Value("${spring.datasource.second.url}")
    private String url;

    @Value("${spring.datasource.second.username}")
    private String user;

    @Value("${spring.datasource.second.password}")
    private String password;

    @Value("${spring.datasource.second.driverClassName}")
    private String driverClassName;

    @Bean(name = "secondDataSource")
    public DataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(user);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    @Bean(name = "secondTransactionManager")
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean(name = "secondSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/SecondDataSourceMapping.xml"));
        return sessionFactory.getObject();
    }
}

dao/first/FirstDataSourceMapping.java

@Repository
public interface FirstDataSourceMapping {
    List<LinkedHashMap<String, Object>> GetAllRoot();
}

dao/second/SecondDataSourceMapping.java

@Repository
public interface SecondDataSourceMapping {
    List<LinkedHashMap<String, Object>> GetAllTemp();
}

service/DataService.java

@Service
public class DataService {

    @Autowired
    private FirstDataSourceMapping firstDataSourceMapping;

    @Autowired
    private SecondDataSourceMapping secondDataSourceMapping;

    public List<LinkedHashMap<String, Object>> GetAllRoot() {
        return firstDataSourceMapping.GetAllRoot();
    }

    public List<LinkedHashMap<String, Object>> GetAllTemp() {
        return secondDataSourceMapping.GetAllTemp();
    }
}

resources/mapper/FirstDataSourceMapping.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.dao.first.FirstDataSourceMapping">
    <select id="GetAllRoot" resultType="java.util.LinkedHashMap">
        <![CDATA[
            select * from tableroot
        ]]>
    </select>
</mapper>

resources/mapper/SecondDataSourceMapping.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.dao.second.SecondDataSourceMapping">
    <select id="GetAllTemp" resultType="java.util.LinkedHashMap">
        <![CDATA[
            select * from tabletemp
        ]]>
    </select>
</mapper>

action/Controller.java

@RestController
public class Controller {

    @Autowired
    private DataService dataService;

    @RequestMapping(value = "/searchroot", method = RequestMethod.GET)
    public void searchRequstHandler1(HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<LinkedHashMap<String, Object>> result;
        result = dataService.GetAllRoot();
        System.out.println(result);
    }

    @RequestMapping(value = "/searchtemp", method = RequestMethod.GET)
    public void searchRequstHandler2(HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<LinkedHashMap<String, Object>> result;
        result = dataService.GetAllTemp();
        System.out.println(result);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章