SpringBoot MyBatis

Spring Boot + MyBatis 實現多個DataSource配置

蔡維宇 2020/11/30 19:20:57
1226

因在開發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有一些覺得不錯的心得,也會在與大家分享

 

蔡維宇