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 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);
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 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);
Creating a simple spring boot web application:
First, open the Spring initializr https://start.spring.io/
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.
Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(spring-boot-mybatis-multidatasource-mysql-postgresql) file and downloads the project. Then, Extract the Zip file.
Then, import the project on your favourite IDE.
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
Mapper XML is an important file in MyBatis, which contains a set of statements to configure various SQL statements such as select, insert, update, and delete.
<?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
Spring supports exception handling by a global Exception Handler (@ExceptionHandler) with Controller Advice (@ControllerAdvice). This enables a mechanism that makes ResponseEntity work with the type safety and flexibility of @ExceptionHandler.
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); }
}
Application is the entry point that sets up the Spring Boot application. The @SpringBootApplication annotation enables auto-configuration and component scanning.
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:
More....













Comments
Post a Comment