			 	                 "Welcome To Ashok IT"				
			                     "Spring Boot and MicroServices"
                                             Topic : Connecting Multiple Databases
                                                 Date  : 29/05/2025
                                                   (Session - 55)
_____________________________________________________________________________________________________________________________

Connecting Multiple Databases
=============================
* So far we have seen how to connect with single Database in Spring Boot Application Using Spring Data JPA.

* Spring Data JPA Provies the lot of abstraction on ORM Tools and reduces the burden for the programmers.

* As we are already familiar Spring/Spring Boot internally obtaining the Datasource object.

* Datasource Object is required for EntityManager object and EntityManager Object is required for TranscationManager object     and finally it can be wrapped JPARepository.

* Spring boot allows you to connect to multiple databases by configuring multiple data sources in a single spring boot          application using JPA. 

* Spring boot enables repositories to connect to multiple databases using JPA from a single application. Multiple data source   configurations allow multiple database connections to be established in a spring boot application. With application           properties file configurations, spring boot makes it very easy to use multiple databases in a single spring boot              application.

* Each database connection should have its own DataSource, EntityManagerFactory, and PlatformTransactionManager. To connect     multiple databases, each database should be configured in its own spring boot configuration file. Multiple data sources       should be configured for multiple databases.

Advantages
==========
1) Improved scalability: 
========================
* By configuring multiple data sources, you can distribute the load across multiple databases, which can help improve the     scalability of your application.

2) Improved performance:
========================
* By using multiple data sources, you can improve the performance of your application by reducing the load on a single       database and by distributing the load across multiple databases.

3) Improved fault tolerance: 
============================
* If one of your databases goes down, having multiple data sources can help ensure that your application remains available     and can continue to operate using the other available data sources.

4) Improved security:
=====================
* By using multiple data sources, you can improve the security of your application by segregating sensitive data into    separate databases.

5) Support for multiple data types: 
===================================
* With multiple data sources, you can use different types of databases to store different types of data. For example, you can   use a NoSQL database for storing unstructured data and a relational database for storing structured data.

6) Flexibility: 
===============
* Configuring multiple data sources with Spring Boot gives you more flexibility in terms of how you manage your data. You can   choose to use different databases for different purposes or use multiple databases to support different parts of your         application.

Example
=======

Step-1 : Create the Spring Boot Application with below dependencies

        <dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

Step-2 : Configure the multiple datasources in application.properties as below

#Payment Related information will get Stored in MySQLDB
payment.datasource.url=jdbc:mysql://localhost:3306/47_springbootms_db
payment.datasource.username=root
payment.datasource.password=root
payment.datasource.driverClassName=com.mysql.cj.jdbc.Driver

#Order Related information will get Stored in OracleDB
order.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
order.datasource.username=system
order.datasource.password=manager
order.datasource.driverClassName=oracle.jdbc.driver.OracleDriver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true


Step-3: Create several packages in the project as below

        com.multiple.datasource   >>>>>>>>>>>>>>>>>>>> Base package

	com.multiple.datasource.config >>>>>>>>>>>>>>> Configuration Class

        com.multiple.datasource.entity.Payment >>>>>>> Payment Entity(MySQL)             
 
	com.multiple.datasource.entity.OrderDetails >> OrderDetails Entity (Oracle)
    
	com.multiple.datasource.repoistory.Payment >>>> PaymentRespoistory(MySQL)

        com.multiple.datasource.repoistory.Order >>>> OrderRespoistory(Oracle)


Step-4: Develop the below source codes

OrderDetails.java
=================
package com.multiple.datasource.entity.orders;

import java.util.Date;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

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

@Entity
@Table(name = "ORDER_DETAILS") //OracleDatabase
public class OrderDetails {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;

	private String quantity;

	private String price;

	@CreationTimestamp
	private Date created;

	@UpdateTimestamp
	private Date updated;

	public OrderDetails() {

	}

	public OrderDetails(long id, String quantity, String price, Date created, Date updated) {
		super();
		this.id = id;
		this.quantity = quantity;
		this.price = price;
		this.created = created;
		this.updated = updated;
	}

	public long getId() {
		return id;
	}

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

	public String getQuantity() {
		return quantity;
	}

	public void setQuantity(String quantity) {
		this.quantity = quantity;
	}

	public String getPrice() {
		return price;
	}

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

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getUpdated() {
		return updated;
	}

	public void setUpdated(Date updated) {
		this.updated = updated;
	}

	@Override
	public String toString() {
		return "OrderDetails [id=" + id + ", quantity=" + quantity + ", price=" + price + ", created=" + created
				+ ", updated=" + updated + "]";
	}

}


PaymentDetails.java
===================
package com.multiple.datasource.entity.payment;

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

@Entity
@Table(name = "PAYMENT_DETAILS")  //MySql Database
public class PaymentDetails {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;
	private String cardNumber;
	private String cardName;
	private String expiry;
	private String cvv;

	public PaymentDetails() {

	}

	public PaymentDetails(long id, String cardNumber, String cardName, String expiry, String cvv) {
		super();
		this.id = id;
		this.cardNumber = cardNumber;
		this.cardName = cardName;
		this.expiry = expiry;
		this.cvv = cvv;
	}

	public long getId() {
		return id;
	}

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

	public String getCardNumber() {
		return cardNumber;
	}

	public void setCardNumber(String cardNumber) {
		this.cardNumber = cardNumber;
	}

	public String getCardName() {
		return cardName;
	}

	public void setCardName(String cardName) {
		this.cardName = cardName;
	}

	public String getExpiry() {
		return expiry;
	}

	public void setExpiry(String expiry) {
		this.expiry = expiry;
	}

	public String getCvv() {
		return cvv;
	}

	public void setCvv(String cvv) {
		this.cvv = cvv;
	}

	@Override
	public String toString() {
		return "PaymentDetails [id=" + id + ", cardNumber=" + cardNumber + ", cardName=" + cardName + ", expiry="
				+ expiry + ", cvv=" + cvv + "]";
	}

}


OrderRepoistory.java
====================
package com.multiple.datasource.repository.order;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import com.multiple.datasource.entity.orders.OrderDetails;

@Repository
public interface OrderRepository extends JpaRepository<OrderDetails, Long>{
	
	@Query(value="select u from OrderDetails as u")
	List<OrderDetails> getOrderList();

}

PaymentRepoistory.java
======================
package com.multiple.datasource.repository.payment;

import org.springframework.data.jpa.repository.JpaRepository;

import com.multiple.datasource.entity.payment.PaymentDetails;

public interface PaymentRepository extends JpaRepository<PaymentDetails, Long>{

}


OrderDataSource.java
====================
package com.multiple.datasource.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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;

@Configuration
@EnableJpaRepositories(
		basePackages = "com.multiple.datasource.repository.order",
		entityManagerFactoryRef = "orderEntityManagerFactory",
		transactionManagerRef = "getOrderPTM")
public class OrderDataSource { //OracleDatabase
	
	@Bean
	@ConfigurationProperties(prefix = "order.datasource") //this is used for reading config from application.properties
	DataSourceProperties getDatasourceProperties() {
		return new DataSourceProperties();
	}
	
	@Bean
	@ConfigurationProperties("order.datasource.configuration")
	DataSource getDatasource() {
		return getDatasourceProperties().initializeDataSourceBuilder().type(BasicDataSource.class).build();
	}
	
	@Bean(name="orderEntityManagerFactory")
	//EntityManagerFactory(I) >>>>>> LocalContainerEntityManagerFactoryBean(IC)
	//EntityManagerFactoryBuilder class for building EntityManagerFactory Object
	LocalContainerEntityManagerFactoryBean getLocalEMF(EntityManagerFactoryBuilder builder) {
		return builder.dataSource(getDatasource())
				      .packages("com.multiple.datasource.entity.orders")
				      .build();
	}
	
	@Bean
	PlatformTransactionManager getOrderPTM(
			final @Qualifier("orderEntityManagerFactory") LocalContainerEntityManagerFactoryBean containerEntityManagerFactoryBean) {
		return new JpaTransactionManager(containerEntityManagerFactoryBean.getObject());
	}

}

PaymentDataSource.java
======================
package com.multiple.datasource.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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;

@Configuration
@EnableJpaRepositories(basePackages = "com.multiple.datasource.repository.payment", 
                       entityManagerFactoryRef = "paymentEntityManagerFactory", 
                       transactionManagerRef = "getPlatformTxMgr")
public class PaymentDataSource {

	@Bean
	@ConfigurationProperties(prefix = "payment.datasource")
	DataSourceProperties getDataSourceProperty() {
		return new DataSourceProperties();
	}

	@Bean
	@Primary
	@ConfigurationProperties(prefix = "payment.datasource.configuration")
	DataSource getDataSource() {
		return getDataSourceProperty().initializeDataSourceBuilder().type(BasicDataSource.class).build();
	}

	@Bean(name = "paymentEntityManagerFactory")
	@Primary
	LocalContainerEntityManagerFactoryBean paymentEntityManagerFactory(EntityManagerFactoryBuilder builder) {
		return builder.dataSource(getDataSource()).packages("com.multiple.datasource.entity.payment").build();
	}

	@Bean
	@Primary
	PlatformTransactionManager getPlatformTxMgr(
			final @Qualifier("paymentEntityManagerFactory") LocalContainerEntityManagerFactoryBean paymentEntityManagerFactory) {
		return new JpaTransactionManager(paymentEntityManagerFactory.getObject());
	}

}


SpringApplication.java
======================
package com.multiple.datasource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.multiple.datasource.entity.orders.OrderDetails;
import com.multiple.datasource.entity.payment.PaymentDetails;
import com.multiple.datasource.repository.order.OrderRepository;
import com.multiple.datasource.repository.payment.PaymentRepository;

@SpringBootApplication
public class SpringMultipleDatasoureApplication implements CommandLineRunner{

	@Autowired
	private OrderRepository orderRepository;
	
	@Autowired
	private PaymentRepository paymentRepository;
	
	
	public static void main(String[] args) {
		SpringApplication.run(SpringMultipleDatasoureApplication.class, args);
	}
	
	@Override
	public void run(String... args) throws Exception {
		
		//Creating OrderDetails object in oracle database
		OrderDetails od = new OrderDetails();
		od.setPrice("5000");
		od.setQuantity("25");
		od.setCreated(new java.util.Date());
		od.setUpdated(new java.util.Date());
		OrderDetails savedOrder = orderRepository.save(od);
		System.out.println("SavedOrder:::::" + savedOrder);
		
		//Creating PaymentDetails object in mysql database
		PaymentDetails pd = new PaymentDetails();
		pd.setId(123);
		pd.setCardName("Mahesh");
		pd.setCardNumber("123456");
		pd.setCvv("123");
		pd.setExpiry("01/23");
		PaymentDetails savedPayment = paymentRepository.save(pd);
		System.out.println("Saved Payment::::" + savedPayment);
	}
}

application.properties
======================
#Payment Related information will get Stored in MySQLDB
payment.datasource.url=jdbc:mysql://localhost:3306/31_springbootms_db
payment.datasource.username=root
payment.datasource.password=root
payment.datasource.driverClassName=com.mysql.cj.jdbc.Driver

#Order Related information will get Stored in OracleDB
order.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
order.datasource.username=system
order.datasource.password=manager
order.datasource.driverClassName=oracle.jdbc.driver.OracleDriver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true

pom.xml
=======
                <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>


OUTPUT
======

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
[32m :: Spring Boot :: [39m              [2m (v3.1.1)[0;39m

Hibernate: create global temporary table HTE_ORDER_DETAILS(rn_ number(10,0) not null, created timestamp(6), id number(19,0), updated timestamp(6), price varchar2(255 char), quantity varchar2(255 char), primary key (rn_)) on commit delete rows

Hibernate: create table ORDER_DETAILS (id number(19,0) not null, created timestamp(6), price varchar2(255 char), quantity varchar2(255 char), updated timestamp(6), primary key (id))

Hibernate: create sequence ORDER_DETAILS_SEQ start with 1 increment by 50

Hibernate: select ORDER_DETAILS_SEQ.nextval from dual

Hibernate: insert into ORDER_DETAILS (created,price,quantity,updated,id) values (?,?,?,?,?)
SavedOrder:::::OrderDetails [id=1, quantity=25, price=5000, created=Sat Aug 12 07:11:44 IST 2023, updated=Sat Aug 12 07:11:44 IST 2023]
Hibernate: select p1_0.id,p1_0.cardName,p1_0.cardNumber,p1_0.cvv,p1_0.expiry from PAYMENT_DETAILS p1_0 where p1_0.id=?
Hibernate: insert into PAYMENT_DETAILS (cardName,cardNumber,cvv,expiry) values (?,?,?,?)
Saved Payment::::PaymentDetails [id=1, cardNumber=123456, cardName=Mahesh, expiry=01/23, cvv=123]

+++++++++++++++++++++++++++++++++++++CompletedSpring Data Module+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++