Spring Boot + MyBatis 實現多個DataSource配置
因在開發Spring Boot 專案時遇到需要同時存取多個DB的需求, 在這裡與大家分享實際使用上的經驗及source code
開發環境:
Spring Boot + MyBatis + Mysql + Oracle (2種DB)
DataSource 配置:
專案起始的配置與一般Spring Boot與MyBatis的初始配置相同, 如有不清楚的部分可參考其他關於Spring Boot 和 MyBatis 的相關文章
這裡會為MyBatis配置兩個DataSource設定檔, 分別為Primary, Secondary, 分別對應到 MySql 和 Oralce, 需要注意的是@MapperScan的basePackages位置需要再專案的package做出區分,本文在dao下分別新增primary, secondary 2個 subpackage分別代表2個datasource, 由MBG產生的mapper, model也會分別放在subpackage內,這裡附上專案目錄的圖:
在application.properties內會以前綴primary, secondary 分別指定datasource各項設定後, 使用ConfigurationProperties載入設定值, 程式碼如下
PrimaryDataSourceConfig
package com.code4inspire.mybatisdemo.config;
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.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.jdbc.datasource.DataSourceTransactionManager;
@Configuration
@MapperScan(basePackages = "com.code4inspire.mybatisdemo.dao.primary", sqlSessionTemplateRef = "PrimarySessionTemplate")
public class PrimaryDataSourceConfig {
@Bean("PrimaryDataSource")
@Primary
@ConfigurationProperties(prefix="spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "PrimarySessionFactory")
@Primary
public SqlSessionFactory primarySessionFactory(@Qualifier("PrimaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "PrimaryTransactionManager")
@Primary
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("PrimaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "PrimarySessionTemplate")
@Primary
public SqlSessionTemplate primarySessionTemplate(@Qualifier("PrimarySessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
SecondaryDataSourceConfig
package com.code4inspire.mybatisdemo.config;
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.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@Configuration
@MapperScan(basePackages = "com.code4inspire.mybatisdemo.dao.secondary", sqlSessionTemplateRef = "SecondarySessionTemplate")
public class SecondaryDataSourceConfig {
@Bean("SecondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "SecondarySessionFactory")
public SqlSessionFactory secondarySessionFactory(@Qualifier("SecondaryDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "SecondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(
@Qualifier("SecondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "SecondarySessionTemplate")
public SqlSessionTemplate secondarySessionTemplate(
@Qualifier("SecondarySessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
applicaiton.properties
最後附上Unit Test 的程式瑪:
package com.code4inspire.mybatisdemo.dao;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.Assert;
import com.code4inspire.mybatisdemo.dao.primary.mapper.StudentMapper;
import com.code4inspire.mybatisdemo.dao.primary.model.Student;
import com.code4inspire.mybatisdemo.dao.secondary.mapper.PersonMapper;
import com.code4inspire.mybatisdemo.dao.secondary.model.Person;
@SpringBootTest
public class DaoTest {
@Autowired
private StudentMapper studentMapper;
@Autowired
private PersonMapper personMapper;
@Test
void saveAndSelectStudent() {
int studentId = saveStudent();
queryStudent(studentId);
}
@Test
void saveAndSelectPerson() {
int personId = savePerson();
personQuery(personId);
}
int saveStudent() {
Student student = new Student();
student.setFirstName("Tom");
student.setLastName("Chen");
student.setEmail("tomchen@mail.tw");
int id = studentMapper.insert(student);
return id;
}
void queryStudent(int id) {
Optional<Student> optional = studentMapper.selectByPrimaryKey(id);
Assert.isTrue(optional.isPresent(), "Student with id: " + id + " exist " + optional.get());
}
int savePerson() {
Person person = new Person();
person.setFirstName("David");
person.setLastName("Tsai");
person.setGender("M");
int insert = personMapper.insert(person);
return insert;
}
void personQuery(int id) {
long idL = id;
Optional<Person> optional = personMapper.selectByPrimaryKey(idL);
Assert.isTrue(optional.isPresent(), "Person with id: " + id + " exist " + optional.get());
}
}
總結:
專案規劃在ORM框架挑選上,MyBatis相較於Hibernamte而言在使用上也較為彈性,若大家在專案實務上有用到MyBatis,並且有同時存取多個DataSource存取上的需要,不妨參考以上範例, 若之後再使用MyBatis有一些覺得不錯的心得,也會在與大家分享