Como ter múltiplos datasources em seu projeto spring boot
Tags: spring, java, datasource, jpa
Published: 2023-09-24 01:00:39

---
title: Como ter múltiplos datasources em seu projeto spring boot
published: true
description: 
tags: spring, java, datasource, jpa
# cover_image: ![Two sheeps](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5xzw2hn5nfj96zjss8l.jpg)
---

Volta e meia, nos encontramos em [_corner cases_](https://en.wikipedia.org/wiki/Corner_case) no nosso dia-a-dia
Recentemente tive que, de forma não usual, acessar duas bases no mesmo projeto java com spring data.   

Para resolver essa necessidade, tive que realizar algumas alterações na configuração.   
Irei mostrar nessa receita rápida uma forma de atingir esse resultado.   

#### Mise en Place
![mise en place](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tl4y37p59km0dho1dlc6.jpg) Foto de <a href="https://unsplash.com/pt/@rudy_issa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Rudy Issa</a> na <a href="https://unsplash.com/pt-br/fotografias/KVacTm0QeEA?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>

Para acompanhar esse post você pode seguir a seguinte receita, alguns itens podem ser substituídos por outros análogos ;)   
- Java 17
- Spring Boot 3.1.3. Obrigatório.
- Mysql
- Editor de texto da tua escolha.
- Docker

O projeto final está disponível no [github](https://github.com/felipejsm/rarefish).   


#### Montando o projeto

Em [Spring Initializr](https://start.spring.io/), adicionamos as seguintes dependências:
- Spring Data JPA
- MySQL Driver
- Lombok

![Spring Initializr](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbbxrf13n8abhikquoep.png)

Abrindo nosso projeto em algum editor de código/IDE, iremos criar a mesma estrutura de pacote que está no github, nada de [_Clean Architecture_](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html), pois não é objeto desse _post_.

O que nos interessa são os pacote _config_ e o arquivo _application.properties_.

Os pacotes:
![Estrutura de pacotes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wp05zyzg5mju5n5db07d.png)

#### Arquivo de configuração do projeto

Para cada base de dados teremos uma url, um usuário e uma senha.
Por questão de simplicidade, não utilizaremos [variáveis de ambiente](https://www.baeldung.com/spring-boot-properties-env-variables) para esconder informações sensíveis.

```
#### FISH
spring.first-datasource.url=jdbc:mysql://127.0.0.1:3309/rare_fishes_db?useSSL=false&allowPublicKeyRetrieval=true
spring.first-datasource.username=user
spring.first-datasource.password=pass

#### SHIPPING
spring.second-datasource.url=jdbc:mysql://127.0.0.1:3307/shipping_db?useSSL=false&allowPublicKeyRetrieval=true
spring.second-datasource.username=user
spring.second-datasource.password=pass
```
#### Datasources
O prato principal é a configuração de nossa aplicação.
Como dito anteriormente, cada base terá a tua respectiva classe de configuração de dados

Vamos nos debruçar sobre a primeira classe: **PrimaryDataSourceConfiguration**

Iremos perceber um padrão onde as anotações, beans e outras estruturas, estão sendo feitas manualmente.
Normalmente, é algo transparente, o framework assegura o devido funcionamento. Como estamos tratando de um caso que foge da regra, precisamos realizar esse trabalho.

_PrimaryDataSourceConfiguration_ é uma classe de configuração com algumas coisas especiais, primeira delas é a anotação [@EnableTransactionManagement](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/EnableTransactionManagement.html). 
Essa anotação indica ao Spring o datasource para realizar o gerenciamento de transações.
Bem, como assim?
Transações na base de dados são unidades de trabalho que contém instruções/operações realizadas no banco.
O Spring realiza isso para você de forma transparente, seja o _commit_ ou o _rollback_ de uma operação, por exemplo.
Claro, existem outros tipos de transações, sugiro essa [leitura](https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html).

@EnableJpaRepositories, tem a responsabilidade de identificar e habilitar os repositórios JPA.
Ela possui diversos elementos opcionais.
No caso, temos 3 e eles estão relacionados com beans que criaremos a seguir.

#### entityManagerFactoryRef e o Bean LocalContainerEntityManagerFactoryBean 
Criaremos um bean _LocalContainerEntityManagerFactoryBean_ e faremos referência dele em _entityManagerFactoryRef_.
O Bean em questão servirá para configuramos o nosso _datasource_, incluímos o vendor jpa(Hibernate, EclipseLink, etc), o próprio datasource, etc.
```java
@Autowired
	private Environment env;

	@Bean
	@Primary
	public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
		LocalContainerEntityManagerFactoryBean em = 
			new LocalContainerEntityManagerFactoryBean();
		em.setDataSource(primaryDataSource());
		em.setPackagesToScan(
				"com.nssp.rarefish.model.fish");
		HibernateJpaVendorAdapter vendorAdapter 
			= new HibernateJpaVendorAdapter();
		em.setJpaVendorAdapter(vendorAdapter);
		HashMap<String, Object> properties = new HashMap<>();
		properties.put("hibernate.dialect",
				env.getProperty("spring.jpa.properties.hibernate.dialect"));
		properties.put("spring.jpa.generate-ddl",
				env.getProperty("spring.jpa.generate-ddl"));
		properties.put("hibernate.hbm2ddl.auto",
				env.getProperty("spring.jpa.hibernate.ddl-auto"));
				em.setJpaPropertyMap(properties);
				return em;

	}
```

#### transactionManagerRef e o Bean primaryTransactionManager
Utilizando nosso bean criado no passo anterior, estamos habilitando em nossa aplicação a infraestrutura necessária para realizar operações como criar, dar rollback, efetuar commit, das transações.
```java
@Primary
	@Bean
	public PlatformTransactionManager primaryTransactionManager() {
		JpaTransactionManager transactionManager
			= new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(
				primaryEntityManager().getObject());
		return transactionManager;
	}
```
#### Datasource
Instanciamos o nosso _DriverManagerDataSource_ com as informações de URL, de nome de usuário e de senha.
```java
@Primary
	@Bean
	public DataSource primaryDataSource() {
		DriverManagerDataSource dataSource 
			= new DriverManagerDataSource();
		dataSource.setDriverClassName(
				env.getProperty("spring.datasource.drive-class-name"));
		dataSource.setUrl(env.getProperty("spring.first-datasource.url"));
		dataSource.setUsername(env.getProperty("spring.first-datasource.username"));
		dataSource.setPassword(env.getProperty("spring.first-datasource.password"));
		return dataSource;
	}

```
Dica: podemos substituir dessa forma pela seguinte
```java
@Primary
@ConfigurationProperties(prefix = "spring.first-datasource")
@Bean
	public DataSource primaryDataSource() {
            return DataSourceBuilder.create().build();
}
```

#### Juntando tudo
```java
package com.nssp.rarefish.config;

import java.util.HashMap;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
	entityManagerFactoryRef = "primaryEntityManager",
	transactionManagerRef = "primaryTransactionManager",
	basePackages = {"com.nssp.rarefish.model.fish"})
public class PrimaryDataSourceConfiguration {
	@Autowired
	private Environment env;

	@Bean
	@Primary
	public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
		LocalContainerEntityManagerFactoryBean em = 
			new LocalContainerEntityManagerFactoryBean();
		em.setDataSource(primaryDataSource());
		em.setPackagesToScan(
				"com.nssp.rarefish.model.fish");
		HibernateJpaVendorAdapter vendorAdapter 
			= new HibernateJpaVendorAdapter();
		em.setJpaVendorAdapter(vendorAdapter);
		HashMap<String, Object> properties = new HashMap<>();
		properties.put("hibernate.dialect",
				env.getProperty("spring.jpa.properties.hibernate.dialect"));
		properties.put("spring.jpa.generate-ddl",
				env.getProperty("spring.jpa.generate-ddl"));
		properties.put("hibernate.hbm2ddl.auto",
				env.getProperty("spring.jpa.hibernate.ddl-auto"));
				em.setJpaPropertyMap(properties);
				return em;

	}

	@Primary
	@Bean
	public DataSource primaryDataSource() {
		DriverManagerDataSource dataSource 
			= new DriverManagerDataSource();
		dataSource.setDriverClassName(
				env.getProperty("spring.datasource.drive-class-name"));
		dataSource.setUrl(env.getProperty("spring.first-datasource.url"));
		dataSource.setUsername(env.getProperty("spring.first-datasource.username"));
		dataSource.setPassword(env.getProperty("spring.first-datasource.password"));
		return dataSource;
	}

	@Primary
	@Bean
	public PlatformTransactionManager primaryTransactionManager() {
		JpaTransactionManager transactionManager
			= new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(
				primaryEntityManager().getObject());
		return transactionManager;
	}
}
```
E com isso, temos 2 _datasources_ de bancos de dados distintos prontos a serem utilizados!

Obrigado!