java - محل - موقع spring




ربيع التمهيد تكوين واستخدام اثنين DataSources (7)

إليك مقالة مثيرة للاهتمام حول كيفية الاتصال بقاعدتي بيانات باستخدام نقطة انطلاق وتنفيذ المعاملات الموزعة عليها.

https://link.medium.com/hcOZlM1HuY

أنا جديدة على الربيع والربيع التمهيد. كيف يمكنني تكوين واستخدام اثنين من مصادر البيانات؟

على سبيل المثال هنا هو ما لدي لمصدر البيانات الأول:

application.properties

#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver

#second db ...

فئة التطبيق

@SpringBootApplication
public class SampleApplication
{
    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}

كيف يمكنني تعديل application.properties لإضافة مصدر بيانات آخر؟ كيف يمكنني تشغيله تلقائيًا ليستخدمه مستودع مختلف؟


اضطررت أيضًا إلى إعداد اتصال بمصادر بيانات من تطبيق Spring Boot ، ولم يكن الأمر سهلاً - لم ينجح الحل المذكور في وثائق Spring Boot . بعد فترة طويلة من الحفر عبر الإنترنت ، نجحت وأصبحت الفكرة الرئيسية مأخوذة من هذا المقال ومجموعة من الأماكن الأخرى.

الحل التالي مكتوب في Kotlin ويعمل مع Spring Boot 2.1.3 و Hibernate Core 5.3.7 . كانت المشكلة الرئيسية أنه لا يكفي فقط إعداد تهيئات DataSource مختلفة ، ولكن كان من الضروري أيضًا تكوين EntityManagerFactory و TransactionManager لكلتا قواعد البيانات.

فيما يلي التكوين لقاعدة البيانات (الأساسية) الأولى:

#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

وهذا هو التكوين لقاعدة البيانات الثانية:

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;
}
}

خصائص مصادر البيانات تشبه هذا:

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;
}
}

كانت المشكلة المتعلقة بالخصائص هي أنني اضطررت إلى تحديد jdbc-url بدلاً من url لأنه بخلاف ذلك كان لدي استثناء.

ملاحظة: قد يكون لديك أيضًا مخططات تسمية مختلفة في قواعد البيانات الخاصة بك ، وهذا هو الحال بالنسبة لي. نظرًا لأن Hibernate 5 لا يدعم جميع أنظمة التسمية السابقة ، فقد اضطررت إلى استخدام حل من هذه الإجابة - ربما سيساعد ذلك أيضًا شخص ما.


لقد استخدمت mybatis - springboot 2.0 tech stack ، الحل:

//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;
        }



    }

ملاحظة: 1) @ الابتدائية -> @primary

2) ---. "jdbc-url" في الخصائص -> بعد ترحيل Spring Boot 2.0: مطلوب jdbcUrl مع driverClassName


ها أنت ذا

#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();
}

يعمل التعليق التوضيحيPrimary عند استخدامه مقابل طريقة مثل أدناه بشكل جيد إذا كان مصدر البيانات اثنين في نفس الموقع / الخادم 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();
}

إذا كانت مصادر البيانات على خوادم مختلفة ، فمن الأفضل استخدامComponent مع التعليقات التوضيحية @Primary . يعمل مقتطف الشفرة التالي جيدًا على مصدرين مختلفين للبيانات في مواقع مختلفة

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();
    }

}

تحديث 2018-01-07 مع الربيع التمهيد 1.5.8.RELEASE

لا توفر معظم الإجابات كيفية استخدامها (كمصدر بيانات بحد ذاته ومعاملة) ، فقط كيفية تكوينها.

يمكنك الاطلاع على المثال القابل للتشغيل وبعض التوضيح في https://www.surasint.com/spring-boot-with-multiple-databases-example/

قمت بنسخ بعض الكود هنا.

أولاً عليك تعيين application.properties مثل هذا

#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

ثم حددهم كمزودي (@ Bean) مثل هذا:

@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();
}

لاحظ أن لديBean (name = "datasource1") وBean (name = "datasource2") ، ثم يمكنك استخدامه عندما نحتاج إلى مصدر بيانات كـQualifier ("datasource1") وQualifier ("datasource2") ، فمثلا

@Qualifier("datasource1")
@Autowired
private DataSource dataSource;

إذا كنت تهتم بالمعاملة ، فيجب عليك تحديد DataSourceTransactionManager لكل منهما ، مثل هذا:

@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;
}

ثم يمكنك استخدامه مثل

@Transactional //this will use the first datasource because it is @primary

أو

@Transactional("tm2")

هذا يجب أن يكون كافيا. انظر المثال والتفاصيل في الرابط أعلاه.


# 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();
    }




spring-boot