Spring Boot, MyBatis Multiple Datasources Example
In this section, we will show you how we can use multiple data sources in Spring Boot, MyBatis application.
Technologies used:
- Spring Boot 3.0.2
- MyBatis 3
- Java 17
- Maven
- PostgreSQL driver and database
- MySQL driver and database
Database setup
PostgreSQL - Create database and table.
Create a database in the PostgreSQL. CREATE DATABASE userdb;
Then, create table users. CREATE TABLE IF NOT EXISTS public.users( id serial primary key, email VARCHAR(40) not null, first_name VARCHAR(40) not null, last_name VARCHAR(40) not null);
CREATE DATABASE userdb;
CREATE TABLE IF NOT EXISTS public.users(id serial primary key,email VARCHAR(40) not null,first_name VARCHAR(40) not null,last_name VARCHAR(40) not null);
MySQL - Create database and table.
Create a database in the MySQL. CREATE DATABASE studentdb;
Then, create table students. CREATE TABLE IF NOT EXISTS students( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR (200) NOT NULL, email VARCHAR (200) NOT NULL, school_name VARCHAR (200) NOT NULL);
CREATE DATABASE studentdb;
CREATE TABLE IF NOT EXISTS students(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR (200) NOT NULL,email VARCHAR (200) NOT NULL,school_name VARCHAR (200) NOT NULL);
Creating a simple spring boot web application:
Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact spring-boot-mybatis-multidatasource-mysql-postgresql. Here I selected the Maven project - language Java 17 - Spring Boot 3.0.2 and add Spring web dependency, MyBatis Framework, MySQL Driver, and PostgreSQL Driver.
Final Project directory:
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.knf.dev.demo</groupId> <artifactId>spring-boot-mybatis-multidatasource-mysql-postgresql</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-mybatis-multidatasource-mysql-postgresql</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.0</version> </dependency>
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
application.properties
#MySQL configuration propertiesspring.datasource.mysql.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.mysql.jdbc-url=jdbc:mysql://localhost:3306/studentdbspring.datasource.mysql.username=knfuserspring.datasource.mysql.password=root
#PostgreSQL configuration propertiesspring.datasource.postgresql.driver-class-name=org.postgresql.Driverspring.datasource.postgresql.jdbc-url=jdbc:postgresql://localhost:5432/userdbspring.datasource.postgresql.username=postgresspring.datasource.postgresql.password=root
Create PostgreSQLConnMapper.java
package com.knf.dev.demo.config.postgresql;
import org.springframework.stereotype.Component;import java.lang.annotation.*;
@Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface PostgreSQLConnMapper {
String value() default "";}
Create PostgreSQLConfig.java
package com.knf.dev.demo.config.postgresql;
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;
@MapperScan(value = "com.knf.dev.demo", annotationClass= PostgreSQLConnMapper.class, sqlSessionFactoryRef="PostgreSQLSessionFactory")@Configurationpublic class PostgreSQLConfig {
@Bean(name = "PostgreSQLDataSource") @ConfigurationProperties(prefix="spring.datasource.postgresql") public DataSource SecondDataSource() {
return DataSourceBuilder.create().build(); }
@Bean(name = "PostgreSQLSessionFactory") public SqlSessionFactory oracleSqlSessionFactory( @Qualifier("PostgreSQLDataSource") DataSource oracleDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(oracleDataSource); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver(). getResources("mapper/postgresql/*.xml")); sqlSessionFactoryBean.setTypeAliasesPackage("com.knf.dev.demo.model.postgresql"); return sqlSessionFactoryBean.getObject(); }
@Bean(name = "PostgreSQLSessionTemplate") public SqlSessionTemplate oracleSqlSessionTemplate( @Qualifier("PostgreSQLSessionFactory") SqlSessionFactory oracleSessionTemplate) {
return new SqlSessionTemplate(oracleSessionTemplate); }
@Bean(name = "PostgreSQLTransactionManager") public DataSourceTransactionManager PrimaryTransactionManager( @Qualifier("PostgreSQLDataSource") DataSource oracleDataSource) {
return new DataSourceTransactionManager(oracleDataSource); }}
Create MySQLConnMapper.java
package com.knf.dev.demo.config.mysql;
import org.springframework.stereotype.Component;import java.lang.annotation.*;
@Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface MySQLConnMapper { String value() default "";}
Create MySQLConfig.java
package com.knf.dev.demo.config.mysql;
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.ApplicationContext;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;
/*When using multiDB, set the basePackages for mapper classfile scan separately for each DB, and now it doesn'tmatter because you make separate annotations. */@MapperScan(value = "com.knf.dev.demo", annotationClass= MySQLConnMapper.class, sqlSessionFactoryRef="MySQLSessionFactory")@Configurationpublic class MySQLConfig {
//When there are multiple beans of the same type, // give those beans a higher priority @Primary @Bean(name = "MySQLDataSource") @ConfigurationProperties(prefix = "spring.datasource.mysql") public DataSource mysqlDataSource() { return DataSourceBuilder .create() .build(); }
@Primary @Bean(name = "MySQLSessionFactory") public SqlSessionFactory mySqlSessionFactory (@Qualifier("MySQLDataSource") DataSource mysqlDataSource, ApplicationContext applicationContex) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(mysqlDataSource); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver(). getResources("mapper/mysql/*.xml")); sqlSessionFactoryBean.setTypeAliasesPackage("com.knf.dev.demo.model.mysql");
return sqlSessionFactoryBean.getObject(); }
@Primary @Bean(name = "MySQLSessionTemplate") public SqlSessionTemplate mySqlSessionTemplate (@Qualifier("MySQLSessionFactory") SqlSessionFactory mySqlSessionFactory) {
return new SqlSessionTemplate(mySqlSessionFactory); }
@Bean(name = "MysqlTransactionManager") @Primary public DataSourceTransactionManager PrimaryTransactionManager (@Qualifier("MySQLDataSource") DataSource mysqlDataSource) {
return new DataSourceTransactionManager(mysqlDataSource); }}
Create User Bean
package com.knf.dev.demo.model.postgresql;
public class User {
private Integer id; private String firstName; private String lastName; private String email;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }}
Create Student Bean
package com.knf.dev.demo.model.mysql;
public class Student {
private Integer id; private String name; private String schoolName; private String email;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSchoolName() { return schoolName; }
public void setSchoolName(String schoolName) { this.schoolName = schoolName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }}
Create UserMapper.java
package com.knf.dev.demo.mapper.postgresql;
import com.knf.dev.demo.config.postgresql.PostgreSQLConnMapper;import com.knf.dev.demo.model.postgresql.User;import java.util.List;import java.util.Optional;
@PostgreSQLConnMapper("PostgreSQLUserMapper")public interface UserMapper {
List<User> findAllUser();
Optional<User> findUserById(Integer id);
int deleteUserById(Integer id);
int insertUser(User user);
int updateUser(User user);}
Create StudentMapper.java
package com.knf.dev.demo.mapper.mysql;
import com.knf.dev.demo.config.mysql.MySQLConnMapper;import com.knf.dev.demo.model.mysql.Student;import java.util.List;import java.util.Optional;
@MySQLConnMapper("MysqlStudentRepository")public interface StudentMapper {
List<Student> findAllStudent();
Optional<Student> findStudentById(Integer id);
int deleteStudentById(Integer id);
int insertStudent(Student student);
int updateStudent(Student student);}
Create UserMapper.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.knf.dev.demo.mapper.postgresql.UserMapper"> <resultMap type="user" id="result"> <id property="id" column="id" /> <result property="firstName" column="first_name" /> <result property="lastName" column="last_name" /> <result property="email" column="email" /> </resultMap>
<select id="findAllUser" resultType="user" resultMap="result"> SELECT * FROM users </select>
<insert id="insertUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> INSERT INTO users(first_name, last_name, email) VALUES(#{firstName}, #{lastName}, #{email}) </insert>
<update id="updateUser" parameterType="user"> UPDATE users SET first_name = #{firstName}, email = #{email}, last_name = #{lastName} WHERE id = #{id} </update>
<delete id="deleteUserById" parameterType="int"> DELETE FROM users WHERE id = #{id} </delete>
<select id="findUserById" parameterType="int" resultType="user" resultMap="result"> SELECT * FROM users WHERE id = #{id} </select></mapper>
Create StudentMapper.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.knf.dev.demo.mapper.mysql.StudentMapper"> <resultMap type="student" id="result"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="schoolName" column="school_name" /> <result property="email" column="email" /> </resultMap>
<select id="findAllStudent" resultType="student" resultMap="result"> SELECT * FROM students </select>
<insert id="insertStudent" parameterType="student" keyProperty="id" useGeneratedKeys="true"> INSERT INTO students(name, school_name, email) VALUES(#{name}, #{schoolName}, #{email}) </insert>
<update id="updateStudent" parameterType="student"> UPDATE students SET name = #{name}, email = #{email}, school_name = #{schoolName} WHERE id = #{id} </update>
<delete id="deleteStudentById" parameterType="int"> DELETE FROM students WHERE id = #{id} </delete>
<select id="findStudentById" parameterType="int" resultType="student" resultMap="result"> SELECT * FROM students WHERE id = #{id} </select></mapper>
-Exception handling
Create ResourceNotFoundException
package com.knf.dev.demo.exception;
public class ResourceNotFoundException extends RuntimeException{ private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) { super(message); }}
Create GlobalExceptionHandler
package com.knf.dev.demo.exception;
import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.context.request.WebRequest;import java.time.LocalDateTime;
@ControllerAdvicepublic class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<CustomErrorResponse> globalExceptionHandler(Exception ex, WebRequest request) { CustomErrorResponse errors = new CustomErrorResponse(); errors.setTimestamp(LocalDateTime.now()); errors.setError(ex.getMessage()); errors.setStatus(HttpStatus.NOT_FOUND.value()); return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND); }}
Create CustomErrorResponse
package com.knf.dev.demo.exception;
import com.fasterxml.jackson.annotation.JsonFormat;import java.time.LocalDateTime;
public class CustomErrorResponse {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") private LocalDateTime timestamp; private int status; private String error; public LocalDateTime getTimestamp() { return timestamp; } public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getError() { return error; } public void setError(String error) { this.error = error; }}
Create User Controller
package com.knf.dev.demo.controller.postgresql;
import com.knf.dev.demo.exception.ResourceNotFoundException;import com.knf.dev.demo.mapper.postgresql.UserMapper;import com.knf.dev.demo.model.postgresql.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.List;import java.util.Map;
@RestController@RequestMapping("/api/v1")public class UserController { @Autowired private UserMapper userMapper;
// get all users @GetMapping("/users") public List<User> getAllUsers() { return userMapper.findAllUser(); }
// create user rest API @PostMapping("/users") public Map<String, Boolean> createUser(@RequestBody User user) {
Map<String, Boolean> response = new HashMap<>();
Boolean bool = userMapper.insertUser(user) > 0 ? response.put("created", Boolean.TRUE) : response.put("created", Boolean.FALSE);
return response;
}
// get user by id rest api @GetMapping("/users/{id}") public User findUserById(@PathVariable Integer id) {
User user = userMapper.findUserById(id). orElseThrow(() -> new ResourceNotFoundException ("User not exist with id :" + id)); return user; }
// update user rest api @PutMapping("/users/{id}") public Map<String, Boolean> updateUser(@PathVariable Integer id, @RequestBody User userDetails) {
User user = userMapper.findUserById(id) .orElseThrow(() -> new ResourceNotFoundException ("User not exist with id :" + id)); userDetails.setId(id); Map<String, Boolean> response = new HashMap<>();
Boolean bool = userMapper.updateUser(userDetails) > 0 ? response.put("updated", Boolean.TRUE) : response.put("updated", Boolean.FALSE);
return response; }
// delete user rest api @DeleteMapping("/users/{id}") public Map<String, Boolean> deleteUser (@PathVariable Integer id) {
User user = userMapper.findUserById(id) .orElseThrow(() -> new ResourceNotFoundException ("User not exist with id :" + id));
Map<String, Boolean> response = new HashMap<>();
Boolean bool = userMapper.deleteUserById(user.getId()) > 0 ? response.put("deleted", Boolean.TRUE) : response.put("deleted", Boolean.FALSE); return response; }}
Create Student Controller
package com.knf.dev.demo.controller.mysql;
import com.knf.dev.demo.exception.ResourceNotFoundException;import com.knf.dev.demo.mapper.mysql.StudentMapper;import com.knf.dev.demo.model.mysql.Student;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;
import java.util.HashMap;import java.util.List;import java.util.Map;
@RestController@RequestMapping("/api/v1")public class StudentController {
@Autowired private StudentMapper studentMapper;
// get all student @GetMapping("/students") public List<Student> getAllStudent() { return studentMapper.findAllStudent(); } // create student rest API @PostMapping("/students") public Map<String, Boolean> createStudent(@RequestBody Student student) {
Map<String, Boolean> response = new HashMap<>();
Boolean bool = studentMapper.insertStudent(student) > 0 ? response.put("created", Boolean.TRUE) : response.put("created", Boolean.FALSE);
return response;
}
// get student by id rest api @GetMapping("/students/{id}") public Student findStudentById(@PathVariable Integer id) {
Student student = studentMapper.findStudentById(id). orElseThrow(() -> new ResourceNotFoundException ("Student not exist with id :" + id)); return student; }
// update student rest api @PutMapping("/students/{id}") public Map<String, Boolean> updateStudent(@PathVariable Integer id, @RequestBody Student studentDetails) {
Student student = studentMapper.findStudentById(id) .orElseThrow(() -> new ResourceNotFoundException ("Student not exist with id :" + id)); studentDetails.setId(id); Map<String, Boolean> response = new HashMap<>();
Boolean bool = studentMapper.updateStudent(studentDetails) > 0 ? response.put("updated", Boolean.TRUE) : response.put("updated", Boolean.FALSE);
return response; }
// delete student rest api @DeleteMapping("/students/{id}") public Map<String, Boolean> deleteStudent (@PathVariable Integer id) {
Student student = studentMapper.findStudentById(id) .orElseThrow(() -> new ResourceNotFoundException ("Student not exist with id :" + id));
Map<String, Boolean> response = new HashMap<>();
Boolean bool = studentMapper.deleteStudentById(student.getId()) > 0 ? response.put("deleted", Boolean.TRUE) : response.put("deleted", Boolean.FALSE); return response; }}
Run the application - Application.java
package com.knf.dev.demo;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class Application {
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
}
Step1: Download or clone the source code from GitHub to a local machine - Click here
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
OR
Run this Spring boot application from
- IntelliJ IDEA IDE by right click - Run 'Application.main()'
- Eclipse/STS - You can right click the project or the Application.java file and run as java application or Spring boot application.
Step1: Download or clone the source code from GitHub to a local machine - Click here
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
OR
Run this Spring boot application from
- IntelliJ IDEA IDE by right click - Run 'Application.main()'
- Eclipse/STS - You can right click the project or the Application.java file and run as java application or Spring boot application.
Verify User APIs
Add User:
Update User:
Verify Student APIs
Add Student:
Update Student:
Fetch all Student:
Get Student by Id:
Delete Student by Id: