Multiple Data Sources in Spring Data JPA and Spring Boot MVC

On the Eclipse, create a Spring MVC project

Enter Project Information:

  • Name: LearnSpringMVCWithRealApps
  • Group: com.demo
  • Artifact: LearnSpringMVCWithRealApps
  • Description: Learn Spring MVC with Real Apps
  • Package: com.demo

Select the technologies and libraries to be used:

  • Web
  • JPA
  • MySQL

Click Next button to show Site Information for project

Click Finish button to finish create Spring MVC project

<?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>2.6.1</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.demo</groupId>
	<artifactId>LearnSpringMVCWithRealApps</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>LearnSpringMVCWithRealApps</name>
	<description>Learn Spring MVC with Real Apps</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>

		<dependency>
			<groupId>javax.servlet.jsp.jstl</groupId>
			<artifactId>javax.servlet.jsp.jstl-api</artifactId>
			<version>1.2.1</version>
		</dependency>

		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>

		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</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>				

Create new database named database1. In this database create new table named Category as below:

--
-- Table structure for table `category`
--

CREATE TABLE `category` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(250) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- Dumping data for table `category`
--

INSERT INTO `category` (`name`) VALUES
('Category 1'),
('Category 2'),
('Category 3');

Create new database named database2. In this database create new table named Product as below:

--
-- Table structure for table `product`
--

CREATE TABLE `product` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(250) NOT NULL,
  `price` double NOT NULL,
  `status` int(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- Dumping data for table `product`
--

INSERT INTO `product` (`name`, `price`, `status`) VALUES
('Product 1', 4.5, 1),
('Product 2', 7.8, 0),
('Product 3', 11, 0),
('Product 4', 22, 1);
spring.datasource.jdbc-url=jdbc:mysql://localhost:3306/database1
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.seconddatasource.jdbc-url=jdbc:mysql://localhost:3306/database2
spring.seconddatasource.username=root
spring.seconddatasource.password=
spring.seconddatasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.database=default
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true	

server.port=9596

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
spring.mvc.static-path-pattern=/resources/**

Create models classes for tables in database1 and database2 as below:

Create new package named com.demo.database1.models. In this package, create new java class
named Category.java as below:

package com.demo.database1.models;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "category")
public class Category {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	private String name;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}					

Create new package named com.demo.database2.models. In this package, create new java class
named Product.java as below:

package com.demo.database2.models;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "product")
public class Product {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	private String name;

	private double price;

	private boolean status;

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	public boolean isStatus() {
		return status;
	}

	public void setStatus(boolean status) {
		this.status = status;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}						

Create new repository interfaces as below:

Create new package named com.demo.database1.repositories. In this package create
CategoryRepository interface implements from CrudRepository interface as below:

package com.demo.database1.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.demo.database1.models.Category;

@Repository
public interface CategoryRepository extends CrudRepository<Category, Integer> {
}	

Create new package named com.demo.database2.repositories. In this package create
ProductRepository interface implements from CrudRepository interface as below:

package com.demo.database2.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.demo.database2.models.Product;

@Repository
public interface ProductRepository extends CrudRepository<Product, Integer> {
}	

Create new package named com.demo.services. In this package, create new services as below:

In com.demo.services package, create CategoryService interface as below:

package com.demo.services;

import com.demo.database1.models.Category;

public interface CategoryService {

	public Iterable<Category> findAll();
	
}	

In com.demo.services package, create CategoryServiceImpl class implements from
CategoryService interfaces as below:

package com.demo.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo.database1.models.Category;
import com.demo.database1.repositories.CategoryRepository;

@Service
public class CategoryServiceImpl implements CategoryService {

	@Autowired
	private CategoryRepository categoryRepository;
	
	@Override
	public Iterable<Category> findAll() {
		return categoryRepository.findAll();
	}

}	

In com.demo.services package, create ProductService interface as below:

package com.demo.services;

import com.demo.database2.models.Product;

public interface ProductService {

	public Iterable<Product> findAll();
	
}	

In com.demo.services package, create ProductServiceImpl class implements from
ProductService interfaces as below:

package com.demo.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo.database2.models.Product;
import com.demo.database2.repositories.ProductRepository;

@Service
public class ProductServiceImpl implements ProductService {

	@Autowired
	private ProductRepository productRepository;
	
	@Override
	public Iterable<Product> findAll() {
		return productRepository.findAll();
	}

}	

Create new package named com.demo.controllers. In this package, create controllers as below:

Create new java class, named DemoController.java

package com.demo.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.demo.services.CategoryService;
import com.demo.services.ProductService;

@Controller
@RequestMapping(value = { "", "demo" })
public class DemoController {

	@Autowired
	private ProductService productService;
	
	@Autowired
	private CategoryService categoryService;
	
	@RequestMapping(value = { "", "index" }, method = RequestMethod.GET)
	public String index(ModelMap modelMap) {
		modelMap.put("products", productService.findAll());
		modelMap.put("categories", categoryService.findAll());
		return "demo/index";
	}
	
}					

Create new folders with path webapp\WEB-INF\views in src\main. In views folder,
create views as below:

Create new folder named demo. Create new jsp file named index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="ISO-8859-1">
		<title>Insert title here</title>
	</head>
	<body>

		<h3>Categories List</h3>
		<table border="1">
			<tr>
				<th>Id</th>
				<th>Name</th>
			</tr>
			<c:forEach var="category" items="${categories }">
				<tr>
					<td>${category.id }</td>
					<td>${category.name }</td>
				</tr>
			</c:forEach>
		</table>
		
		<h3>Product List</h3>
		<table border="1">
			<tr>
				<th>Id</th>
				<th>Name</th>
				<th>Price</th>
				<th>Status</th>
			</tr>
			<c:forEach var="product" items="${products }">
				<tr>
					<td>${product.id }</td>
					<td>${product.name }</td>
					<td>${product.price }</td>
					<td>${product.status }</td>
				</tr>
			</c:forEach>
		</table>

	</body>
</html>

Create new package named com.demo.configurations, create new configurations as below:

In com.demo.configurations package, create new java class named Database1Configuration.java as below:

package com.demo.configurations;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableAutoConfiguration
@EnableTransactionManagement
@EnableJpaRepositories(
	entityManagerFactoryRef = "entityManagerFactory", 
	transactionManagerRef = "transactionManager", 
	basePackages = { "com.demo.database1.repositories" }
)
public class Database1Configuration {

	@Primary
	@Bean(name = "dataSource")
	@ConfigurationProperties(prefix = "spring.datasource")
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

	@Primary
	@Bean(name = "entityManagerFactory")
	public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(EntityManagerFactoryBuilder entityManagerFactoryBuilder, @Qualifier("dataSource") DataSource dataSource) {
		return entityManagerFactoryBuilder.dataSource(dataSource).packages("com.demo.database1.models")
				.persistenceUnit("database1").build();
	}

	@Primary
	@Bean(name = "transactionManager")
	public PlatformTransactionManager platformTransactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
		return new JpaTransactionManager(entityManagerFactory);
	}

}				

In com.demo.configurations package, create new java class named Database2Configuration.java as below:

package com.demo.configurations;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableAutoConfiguration
@EnableTransactionManagement
@EnableJpaRepositories(
	entityManagerFactoryRef = "database2EntityManagerFactory", 
	transactionManagerRef = "database2TransactionManager", 
	basePackages = { "com.demo.database2.repositories" }
)
public class Database2Configuration {

	@Bean(name = "secondDataSource")
	@ConfigurationProperties(prefix = "spring.seconddatasource")
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

	@Bean(name = "database2EntityManagerFactory")
	public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(EntityManagerFactoryBuilder entityManagerFactoryBuilder, @Qualifier("secondDataSource") DataSource dataSource) {
		return entityManagerFactoryBuilder.dataSource(dataSource).packages("com.demo.database2.models")
				.persistenceUnit("database2").build();
	}

	@Bean(name = "database2TransactionManager")
	public PlatformTransactionManager platformTransactionManager(@Qualifier("database2EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
		return new JpaTransactionManager(entityManagerFactory);
	}

}					

Select LearnSpringMVCWithRealAppsApplication.java file in com.demo package, right click
and select Run As/Spring Boot App menu

Access index method in demo controller with following url:
http://localhost:9596/demo/index

Output