Configurando Múltiplos Bancos de Dados no Spring Boot

Minha aplicação possui conexão com um banco de dados remoto (SQLServer), mas gostaria de realizar uma pequena duplicação de certos dados menos voláteis em um banco local e embarcado (H2) para ganhar em termos de desempenho. Minha configuração hoje é a de um único banco, informando no .properties:

spring.jpa.show-sql=true
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:sqlserver://****:1433;databaseName=****
spring.datasource.username=****
spring.datasource.password=****

Ficando a cargo do próprio Spring Boot a criação do EntityManager, TransactionManager e etc. Por isso, depois disso apenas crio meus Repository e pronto. Como posso fazer com que um Repository esteja vinculado a um banco e um segundo a outro banco?

Primeiramente, vamos configurar os dados da conexão com os dois bancos de dados no arquivo application.properties:

#----DATABASE SQLSERVER
spring.jpa.show-sql=true
spring.sqlserver.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.sqlserver.datasource.url=jdbc:sqlserver://****:1433;databaseName=****
spring.sqlserver.datasource.username=****
spring.sqlserver.datasource.password=****

#----DATABASE H2
spring.h2.datasource.url=jdbc:h2:file:~/myh2
spring.h2.datasource.username=sa
spring.h2.datasource.password=
spring.h2.datasource.driver-class-name=org.h2.Driver

Perceba que, diferente da configuração original, foi acrescentado o nome do banco de dados na nomenclatura para diferenciar as conexões. Por fugir da nomenclatura default dos dados de conexão, o Spring Boot não conseguirá iniciar a conexão com os bancos de dados, mas isso será resolvido no passo seguinte.

Depois disso, vamos precisar em nosso pacote model criar um subpacote para conter entidades e repositórios de cada banco. Ficaria algo como ...model.h2.entity e ...model.sqlserver.entity e o mesmo para o repositório. Óbviamente, você poderá criar da maneira que achar melhor, aqui seria só um exemplo possível de separação.

  • br.com.tassioauad.myapp.model
    • h2
      • entity
      • repository
    • sqlserver
      • entity
      • repository

Com essa separação feita, vamos tirar um pouco desse lado automático do Spring Boot para termos mais controle do que está acontecendo e, consequentemente, podermos informar cada pacote ligado a cada banco. Para isso, vamos criar uma classe de configuração para cada banco que estamos lidando:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "h2EntityManagerFactory",
        transactionManagerRef = "h2TransactionManager",
        basePackages = { "br.com.tassioauad.myapp.model.h2.repository" }
)
public class H2Configuration {

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

    @Bean(name = "h2EntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean
    entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("h2DataSource") DataSource dataSource) {
        Map<String, String> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");
        return builder
                .dataSource(dataSource)
                .packages("br.com.tassioauad.myapp.model.h2.entity")
                .persistenceUnit("h2PU")
                .properties(properties)
                .build();
    }

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

}

E agora um para o SQLServer:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "sqlServerEntityManagerFactory",
        transactionManagerRef = "sqlServerTransactionManager",
        basePackages = { "br.com.tassioauad.myapp.model.sqlserver.repository" }
)
public class SqlServerConfiguration {

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

    @Primary
    @Bean(name = "sqlServerEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean
    entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("sqlServerDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("br.com.tassioauad.myapp.model.sqlserver.entity")
                .persistenceUnit("sqlServerPU")
                .build();
    }

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

No método de criação do DataSource, estamos informando através da annotation @ConfigurationProperties(prefix = "spring.sqlserver.datasource") o nome base da conexão com o banco que configuramos no arquivo application.properties.

Repare que na annotation @EnableJpaRepositories, utilizada para informamos detalhes sobre localização dos repositórios e também da conexão, informamos o nome de nosso EntityManagerTransationManager criado pelos métodos e que também informamos um basePackageapontando o pacote de repositórios que relacionados ao banco de dados do caso.

No método de criação do EntityManagerFactory, repare que na invocação do método .packages("br.com.tassioauad.myapp.model.h2.entity") do EntityManagerFactoryBuilderestamos informando o pacote em que se localiza nossas entidades relacionadas ao banco específico.

Por fim, é preciso salietar a necessidade de existir a annotation @Primary nos métodos de pelo menos uma das classes de configuração para informar que é o banco primário ou principal da aplicação. Sendo assim, nada mais é necessário.

Deixe um comentário