java - datasources - spring multiple data sources example
Spring Boot Configure y use dos fuentes de datos (10)
@La anotación primaria cuando se usa contra un método como el siguiente funciona bien si las dos fuentes de datos están en la misma ubicación / servidor db.
@Bean(name = "datasource1")
@ConfigurationProperties("database1.datasource")
@Primary
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "datasource2")
@ConfigurationProperties("database2.datasource")
public DataSource dataSource2(){
return DataSourceBuilder.create().build();
}
Si las fuentes de datos están en servidores diferentes, es mejor usar @Component junto con la anotación @Primary . El siguiente fragmento de código funciona bien en dos fuentes de datos diferentes en diferentes ubicaciones
database1.datasource.url = jdbc:mysql://127.0.0.1:3306/db1
database1.datasource.username = root
database1.datasource.password = mysql
database1.datasource.driver-class-name=com.mysql.jdbc.Driver
database2.datasource1.url = jdbc:mysql://192.168.113.51:3306/db2
database2.datasource1.username = root
database2.datasource1.password = mysql
database2.datasource1.driver-class-name=com.mysql.jdbc.Driver
@Configuration
@Primary
@Component
@ComponentScan("com.db1.bean")
class DBConfiguration1{
@Bean("db1Ds")
@ConfigurationProperties(prefix="database1.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
@Configuration
@Component
@ComponentScan("com.db2.bean")
class DBConfiguration2{
@Bean("db2Ds")
@ConfigurationProperties(prefix="database2.datasource1")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
Soy nuevo en Spring y Spring Boot. ¿Cómo puedo configurar y usar dos fuentes de datos?
Por ejemplo, esto es lo que tengo para la primera fuente de datos:
application.properties
#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver
#second db ...
Clase de aplicación
@SpringBootApplication
public class SampleApplication
{
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
¿Cómo modifico
application.properties
para agregar otra fuente de datos?
¿Cómo lo instalo automáticamente para que lo use un repositorio diferente?
Aquí está la solución completa
#First Datasource (DB1)
db1.datasource.url: url
db1.datasource.username:user
db1.datasource.password:password
#Second Datasource (DB2)
db2.datasource.url:url
db2.datasource.username:user
db2.datasource.password:password
Como vamos a obtener acceso a dos bases de datos diferentes (db1, db2), necesitamos configurar cada configuración de fuente de datos por separado, como:
public class DB1_DataSource {
@Autowired
private Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean db1EntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(db1Datasource());
em.setPersistenceUnitName("db1EntityManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<string, object=""> properties = new HashMap<>();
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",
env.getProperty("jdbc.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
@Primary
@Bean
public DataSource db1Datasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("jdbc.driver-class-name"));
dataSource.setUrl(env.getProperty("db1.datasource.url"));
dataSource.setUsername(env.getProperty("db1.datasource.username"));
dataSource.setPassword(env.getProperty("db1.datasource.password"));
return dataSource;
}
@Primary
@Bean
public PlatformTransactionManager db1TransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
db1EntityManager().getObject());
return transactionManager;
}
}
Segunda fuente de datos:
public class DB2_DataSource {
@Autowired
private Environment env;
@Bean
public LocalContainerEntityManagerFactoryBean db2EntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(db2Datasource());
em.setPersistenceUnitName("db2EntityManager");
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<string, object=""> properties = new HashMap<>();
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",
env.getProperty("jdbc.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean
public DataSource db2Datasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("jdbc.driver-class-name"));
dataSource.setUrl(env.getProperty("db2.datasource.url"));
dataSource.setUsername(env.getProperty("db2.datasource.username"));
dataSource.setPassword(env.getProperty("db2.datasource.password"));
return dataSource;
}
@Bean
public PlatformTransactionManager db2TransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
db2EntityManager().getObject());
return transactionManager;
}
}
Aquí puede encontrar el ejemplo completo en mi blog: Spring Boot con configuración de múltiples fuentes de datos
Aquí hay un artículo interesante sobre cómo conectarse a dos bases de datos con springboot y realizar transacciones distribuidas en ellas.
Aqui tienes
#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver
#second db ...
spring.secondDatasource.url = [url]
spring.secondDatasource.username = [username]
spring.secondDatasource.password = [password]
spring.secondDatasource.driverClassName = oracle.jdbc.OracleDriver
@Bean
@Primary
@ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
Consulte la documentación oficial
Crear más de un origen de datos funciona igual que crear el primero. Es posible que desee marcar uno de ellos como @Primary si está utilizando la configuración automática predeterminada para JDBC o JPA (entonces esa será seleccionada por cualquier inyección de @Autowired).
@Bean
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
También tuve que configurar la conexión a 2 fuentes de datos desde la aplicación Spring Boot, y no fue fácil: la solución mencionada en la documentación de Spring Boot no funcionó. Después de una larga búsqueda en Internet, lo hice funcionar y la idea principal fue tomada de este artículo y de muchos otros lugares.
La siguiente solución está escrita en Kotlin y funciona con Spring Boot 2.1.3 e Hibernate Core 5.3.7 . El problema principal era que no bastaba con configurar diferentes configuraciones de DataSource , sino que también era necesario configurar EntityManagerFactory y TransactionManager para ambas bases de datos.
Aquí está la configuración para la primera base de datos (Primaria):
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "firstDbEntityManagerFactory",
transactionManagerRef = "firstDbTransactionManager",
basePackages = ["org.path.to.firstDb.domain"]
)
@EnableTransactionManagement
class FirstDbConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.firstDb")
fun firstDbDataSource(): DataSource {
return DataSourceBuilder.create().build()
}
@Primary
@Bean(name = ["firstDbEntityManagerFactory"])
fun firstDbEntityManagerFactory(
builder: EntityManagerFactoryBuilder,
@Qualifier("firstDbDataSource") dataSource: DataSource
): LocalContainerEntityManagerFactoryBean {
return builder
.dataSource(dataSource)
.packages(SomeEntity::class.java)
.persistenceUnit("firstDb")
// Following is the optional configuration for naming strategy
.properties(
singletonMap(
"hibernate.naming.physical-strategy",
"org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"
)
)
.build()
}
@Primary
@Bean(name = ["firstDbTransactionManager"])
fun firstDbTransactionManager(
@Qualifier("firstDbEntityManagerFactory") firstDbEntityManagerFactory: EntityManagerFactory
): PlatformTransactionManager {
return JpaTransactionManager(firstDbEntityManagerFactory)
}
}
Y esta es la configuración para la segunda base de datos:
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "secondDbEntityManagerFactory",
transactionManagerRef = "secondDbTransactionManager",
basePackages = ["org.path.to.secondDb.domain"]
)
@EnableTransactionManagement
class SecondDbConfig {
@Bean
@ConfigurationProperties("spring.datasource.secondDb")
fun secondDbDataSource(): DataSource {
return DataSourceBuilder.create().build()
}
@Bean(name = ["secondDbEntityManagerFactory"])
fun secondDbEntityManagerFactory(
builder: EntityManagerFactoryBuilder,
@Qualifier("secondDbDataSource") dataSource: DataSource
): LocalContainerEntityManagerFactoryBean {
return builder
.dataSource(dataSource)
.packages(EntityFromSecondDb::class.java)
.persistenceUnit("secondDb")
.build()
}
@Bean(name = ["secondDbTransactionManager"])
fun secondDbTransactionManager(
@Qualifier("secondDbEntityManagerFactory") secondDbEntityManagerFactory: EntityManagerFactory
): PlatformTransactionManager {
return JpaTransactionManager(secondDbEntityManagerFactory)
}
}
Las propiedades de las fuentes de datos son así:
spring.datasource.firstDb.jdbc-url=
spring.datasource.firstDb.username=
spring.datasource.firstDb.password=
spring.datasource.secondDb.jdbc-url=
spring.datasource.secondDb.username=
spring.datasource.secondDb.password=
El problema con las propiedades era que tenía que definir jdbc-url en lugar de url porque de lo contrario tenía una excepción.
ps También podría tener diferentes esquemas de nombres en sus bases de datos, que fue el caso para mí. Como Hibernate 5 no es compatible con todos los esquemas de nombres anteriores, tuve que usar la solución de esta respuesta , tal vez también ayude a alguien también.
Utilicé mybatis - springboot 2.0 tech stack, solución:
//application.properties - start
sp.ds1.jdbc-url=jdbc:mysql://localhost:3306/mydb?useSSL=false
sp.ds1.username=user
sp.ds1.password=pwd
sp.ds1.testWhileIdle=true
sp.ds1.validationQuery=SELECT 1
sp.ds1.driverClassName=com.mysql.jdbc.Driver
sp.ds2.jdbc-url=jdbc:mysql://localhost:4586/mydb?useSSL=false
sp.ds2.username=user
sp.ds2.password=pwd
sp.ds2.testWhileIdle=true
sp.ds2.validationQuery=SELECT 1
sp.ds2.driverClassName=com.mysql.jdbc.Driver
//application.properties - end
//configuration class
@Configuration
@ComponentScan(basePackages = "com.mypkg")
public class MultipleDBConfig {
public static final String SQL_SESSION_FACTORY_NAME_1 = "sqlSessionFactory1";
public static final String SQL_SESSION_FACTORY_NAME_2 = "sqlSessionFactory2";
public static final String MAPPERS_PACKAGE_NAME_1 = "com.mypg.mymapper1";
public static final String MAPPERS_PACKAGE_NAME_2 = "com.mypg.mymapper2";
@Bean(name = "mysqlDb1")
@Primary
@ConfigurationProperties(prefix = "sp.ds1")
public DataSource dataSource1() {
System.out.println("db1 datasource");
return DataSourceBuilder.create().build();
}
@Bean(name = "mysqlDb2")
@ConfigurationProperties(prefix = "sp.ds2")
public DataSource dataSource2() {
System.out.println("db2 datasource");
return DataSourceBuilder.create().build();
}
@Bean(name = SQL_SESSION_FACTORY_NAME_1)
@Primary
public SqlSessionFactory sqlSessionFactory1(@Qualifier("mysqlDb1") DataSource dataSource1) throws Exception {
System.out.println("sqlSessionFactory1");
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setTypeHandlersPackage(MAPPERS_PACKAGE_NAME_1);
sqlSessionFactoryBean.setDataSource(dataSource1);
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
@Bean(name = SQL_SESSION_FACTORY_NAME_2)
public SqlSessionFactory sqlSessionFactory2(@Qualifier("mysqlDb2") DataSource dataSource2) throws Exception {
System.out.println("sqlSessionFactory2");
SqlSessionFactoryBean diSqlSessionFactoryBean = new SqlSessionFactoryBean();
diSqlSessionFactoryBean.setTypeHandlersPackage(MAPPERS_PACKAGE_NAME_2);
diSqlSessionFactoryBean.setDataSource(dataSource2);
SqlSessionFactory sqlSessionFactory = diSqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
@Bean
@Primary
public MapperScannerConfigurer mapperScannerConfigurer1() {
System.out.println("mapperScannerConfigurer1");
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage(MAPPERS_PACKAGE_NAME_1);
configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_1);
return configurer;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer2() {
System.out.println("mapperScannerConfigurer2");
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage(MAPPERS_PACKAGE_NAME_2);
configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_2);
return configurer;
}
}
Nota: 1) @Primary -> @primary
2) ---. "Jdbc-url" en propiedades -> Después de la migración de Spring Boot 2.0: se requiere jdbcUrl con driverClassName
Actualice 2018-01-07 con Spring Boot 1.5.8.
La mayoría de las respuestas no proporcionan cómo usarlas (como fuente de datos en sí y como transacción), solo cómo configurarlas.
Puede ver el ejemplo ejecutable y alguna explicación en https://www.surasint.com/spring-boot-with-multiple-databases-example/
Copié un código aquí.
Primero debe configurar application.properties como este
#Database
database1.datasource.url=jdbc:mysql://localhost/testdb
database1.datasource.username=root
database1.datasource.password=root
database1.datasource.driver-class-name=com.mysql.jdbc.Driver
database2.datasource.url=jdbc:mysql://localhost/testdb2
database2.datasource.username=root
database2.datasource.password=root
database2.datasource.driver-class-name=com.mysql.jdbc.Driver
Luego defínalos como proveedores (@Bean) así:
@Bean(name = "datasource1")
@ConfigurationProperties("database1.datasource")
@Primary
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "datasource2")
@ConfigurationProperties("database2.datasource")
public DataSource dataSource2(){
return DataSourceBuilder.create().build();
}
Tenga en cuenta que tengo @Bean (name = "datasource1") y @Bean (name = "datasource2"), luego puede usarlo cuando necesitemos datasource como @Qualifier ("datasource1") y @Qualifier ("datasource2"), por ejemplo
@Qualifier("datasource1")
@Autowired
private DataSource dataSource;
Si le importan las transacciones, debe definir DataSourceTransactionManager para ambos, de esta manera:
@Bean(name="tm1")
@Autowired
@Primary
DataSourceTransactionManager tm1(@Qualifier ("datasource1") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
@Bean(name="tm2")
@Autowired
DataSourceTransactionManager tm2(@Qualifier ("datasource2") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
Entonces puedes usarlo como
@Transactional //this will use the first datasource because it is @primary
o
@Transactional("tm2")
Esto debería ser suficiente. Ver ejemplo y detalles en el enlace de arriba.
Mi requerimiento fue ligeramente diferente pero usé dos fuentes de datos.
He usado dos fuentes de datos para las mismas entidades JPA del mismo paquete. Uno para ejecutar DDL en el inicio del servidor para crear / actualizar tablas y otro para DML en tiempo de ejecución.
La conexión DDL debe cerrarse después de que se ejecutan las instrucciones DDL, para evitar el uso posterior de superreviones de superusuario en cualquier parte del código.
Propiedades
spring.datasource.url=jdbc:postgresql://Host:port
ddl.user=ddluser
ddl.password=ddlpassword
dml.user=dmluser
dml.password=dmlpassword
spring.datasource.driver-class-name=org.postgresql.Driver
Clases de configuración de origen de datos
// Primera clase de configuración para la fuente de datos DDL
public class DatabaseDDLConfig {
@Bean
public LocalContainerEntityManagerFactoryBean ddlEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
PersistenceProvider persistenceProvider = new
org.hibernate.jpa.HibernatePersistenceProvider();
entityManagerFactoryBean.setDataSource(ddlDataSource());
entityManagerFactoryBean.setPackagesToScan(new String[] {
"com.test.two.data.sources"});
HibernateJpaVendorAdapter vendorAdapter = new
HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect",
"org.hibernate.dialect.PostgreSQLDialect");
properties.put("hibernate.physical_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.
SpringPhysicalNamingStrategy");
properties.put("hibernate.implicit_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.
SpringImplicitNamingStrategy");
properties.put("hibernate.hbm2ddl.auto", "update");
entityManagerFactoryBean.setJpaPropertyMap(properties);
entityManagerFactoryBean.setPersistenceUnitName("ddl.config");
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
return entityManagerFactoryBean;
}
@Bean
public DataSource ddlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("ddl.user");
dataSource.setPassword(env.getProperty("ddl.password"));
return dataSource;
}
@Bean
public PlatformTransactionManager ddlTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(ddlEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
// Segunda clase de configuración para la fuente de datos DML
public class DatabaseDMLConfig {
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dmlEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
PersistenceProvider persistenceProvider = new org.hibernate.jpa.HibernatePersistenceProvider();
entityManagerFactoryBean.setDataSource(dmlDataSource());
entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.two.data.sources" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setJpaProperties(defineJpaProperties());
entityManagerFactoryBean.setPersistenceUnitName("dml.config");
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
return entityManagerFactoryBean;
}
@Bean
@Primary
public DataSource dmlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(envt.getProperty("spring.datasource.url"));
dataSource.setUsername("dml.user");
dataSource.setPassword("dml.password");
return dataSource;
}
@Bean
@Primary
public PlatformTransactionManager dmlTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(dmlEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
// Uso de fuentes de datos DDL en el código.
public class DDLServiceAtStartup {
//Import persistence unit ddl.config for ddl purpose.
@PersistenceUnit(unitName = "ddl.config")
private EntityManagerFactory entityManagerFactory;
public void executeDDLQueries() throws ContentServiceSystemError {
try {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.createNativeQuery("query to create/update table").executeUpdate();
entityManager.flush();
entityManager.getTransaction().commit();
entityManager.close();
//Close the ddl data source to avoid from further use in code.
entityManagerFactory.close();
} catch(Exception ex) {}
}
// Uso de la fuente de datos DML en el código.
public class DDLServiceAtStartup {
@PersistenceUnit(unitName = "dml.config")
private EntityManagerFactory entityManagerFactory;
public void createRecord(User user) {
userDao.save(user);
}
}
# Here ''1stDB'' is the database name
spring.datasource.url=jdbc:mysql://localhost/A
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# Here ''2ndDB'' is the database name
spring.second-datasourcee.url=jdbc:mysql://localhost/B
spring.second-datasource.username=root
spring.second-datasource.password=root
spring.second-datasource.driver-class-name=com.mysql.jdbc.Driver
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource firstDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource secondDataSource() {
return DataSourceBuilder.create().build();
}