如何配置多数据源下的liquibase和hibernate

多数据源下面的liquibase配置和hibernate配置方式。 在实际开发过程中总会碰到一个项目中使用多个数据源的情况。本次方便面系列用最少的代码实现多数据源下面的liquibase和hibernate配置。 项目代码

项目技术栈

  • spring boot
  • hibernate
  • liquibase

maven依赖


maven核心依赖只有4个

  • spring-boot-starter-data-jpa 有hibernate依赖的类库
  • liquibase-core 有liquibase依赖的类库
  • mysql-connector-java 连接mysql的类库
  • spring-boot-starter-web web库


项目代码


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

springboot 的properties配置文件

项目代码

server:
    port: 13003
    servlet-path: /spring

multiple:
    datasource:
        ds1:
            url: jdbc:mysql://127.0.0.1:3306/sb_multiple_ds_db1?characterEncoding=UTF-8&useSSL=false&createDatabaseIfNotExist=true
            username: root
            password:
            driver: com.mysql.jdbc.Driver
            test-on-borrow: true
            test-while-idle: true
            validation-query: SELECT 1;
            maxActive: 1
            maxIdle: 20
            minIdle: 15
            initialSize: 1
            liquibase:
                changeLog: "classpath:db/changelog/master.xml"
                defaultSchema: "sb_multiple_ds_db1"
                dropFirst: false
        ds2:
            url: jdbc:mysql://127.0.0.1:3306/sb_multiple_ds_db2?characterEncoding=UTF-8&useSSL=false&createDatabaseIfNotExist=true
            username: root
            password:
            driver: com.mysql.jdbc.Driver
            test-on-borrow: true
            test-while-idle: true
            validation-query: SELECT 1;
            maxActive: 1
            maxIdle: 20
            minIdle: 15
            initialSize: 1
            liquibase:
                changeLog: "classpath:db/changelog2/master.xml"
                defaultSchema: "sb_multiple_ds_db2"
                dropFirst: false



spring:
    datasource:
        tomcat:
            test-on-borrow: true
            validation-query: SELECT 1
            max-active: 50
            max-idle: 20
            min-idle: 15
            initial-size: 1

logging:
    level:
        com.hibernate.demo: DEBUG
        org.hibernate.SQL: DEBUG

datasource 的java类

项目代码


    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "multiple.datasource.ds1")
    public DataSource primaryDataSource() {
        DataSource ds = DataSourceBuilder.create().build();
        return ds;
    }

    @Bean(name = "secondDataSource")
    @ConfigurationProperties(prefix = "multiple.datasource.ds2")
    public DataSource secondDataSource() {
        DataSource ds2 = DataSourceBuilder.create().build();
        return ds2;
    }

liquibase 多数据源配置(基于 SpringLiquibase方法)

liquibase 配置方法有两种,一种是实现 SpringLiquibase 的类, springboot会在启动的时候自动加载 liquibase的xml实现数据库初始化。但是有一个缺点,就是只支持一种数据源。代码如下:
项目代码



    @Bean(name = "liquibase")
    public SpringLiquibase primaryLiquibase(){
        return createSpringLiquibase(primaryDataSource(), primaryLiquibasePropertiese());
    }

    @Bean
    @ConfigurationProperties(prefix = "multiple.datasource.ds1.liquibase")
    public LiquibaseProperties primaryLiquibasePropertiese(){
        return new LiquibaseProperties();
    }


    private SpringLiquibase createSpringLiquibase(DataSource ds,     LiquibaseProperties properties){
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(ds);
        liquibase.setChangeLog(properties.getChangeLog());
        liquibase.setContexts(properties.getContexts());
        liquibase.setDefaultSchema(properties.getDefaultSchema());
        liquibase.setDropFirst(properties.isDropFirst());
        liquibase.setShouldRun(properties.isEnabled());
        liquibase.setLabels(properties.getLabels());
        liquibase.setChangeLogParameters(properties.getParameters());
        liquibase.setRollbackFile(properties.getRollbackFile());
        return liquibase;
    }

liquibase 多数据源配置(基于pom 插件形式)

项目代码


    <profiles>
            <profile>
                <id>datasource-1</id>
                <build>
                    <plugins>
                        <plugin>
                            <groupId>org.liquibase</groupId>
                            <artifactId>liquibase-maven-plugin</artifactId>
                            <configuration>
                                <propertyFile>datasource-1.properties</propertyFile>
                                <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
                            </configuration>
                        </plugin>
                    </plugins>
                </build>
            </profile>
            <profile>
                <id>datasource-2</id>
                <build>
                    <plugins>
                        <plugin>
                            <groupId>org.liquibase</groupId>
                            <artifactId>liquibase-maven-plugin</artifactId>
                            <configuration>
                                <propertyFile>datasource-2.properties</propertyFile>
                                <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
                            </configuration>
                        </plugin>
                    </plugins>
                </build>
            </profile>
        </profiles>

然后在 和pom.xml同级目录中创建datasource-1.properties和datasource-2.properties两个文件,分别如下

datasource-1.properties


    changeLogFile=db/changelog/master.xml
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/sb_multiple_ds_db1?characterEncoding=UTF-8&useSSL=false&createDatabaseIfNotExist=true
    username=root
    password=
    verbose=true
    dropFirst=false

datasource-2.properties


    changeLogFile=db/changelog2/master.xml
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/sb_multiple_ds_db2?characterEncoding=UTF-8&useSSL=false&createDatabaseIfNotExist=true
    username=root
    password=
    verbose=true
    dropFirst=false

多数据源hibernate配置发方法

配置hibernate至少分为四步:

  1. 配置数据源(上面已经有代码)
  2. 根据数据源配置 EntityManagerFactory 的创建。
  3. 根据 EntityManagerFactory 创建 TransactionalManager
  4. 根据 EntityManagerFactory 创建 EntityManager对象 项目代码

    @Primary
    @Bean(name = "primaryEmFactory")
    @Autowired
    public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
            @Qualifier("primaryDataSource") DataSource ds,
            EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(ds)
                .packages(City.class)
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }

    @Bean(name = "primaryTransaction")
    public PlatformTransactionManager localTransactionManager(
            @Qualifier("primaryEmFactory") LocalContainerEntityManagerFactoryBean lc,
            @Qualifier("primaryDataSource") DataSource ds) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(lc.getObject());
        txManager.setDataSource(ds);
        return txManager;
    }
    @Bean(name = "primaryEntityManager")
    public EntityManager primaryEntityManager(
            @Qualifier("primaryEmFactory") LocalContainerEntityManagerFactoryBean le) {
        return le.getObject().createEntityManager();
    }

    public PersistenceExceptionTranslationPostProcessor exceptionTranslator() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    //----------------------------------------------------------
    //---------------------第二个数据源-------------------------
    //----------------------------------------------------------

    @Bean(name = "secondEmFactory")
    @Autowired
    public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
            @Qualifier("secondDataSource") DataSource ds,
            EntityManagerFactoryBuilder builder) {
        System.out.println(ds);
        LocalContainerEntityManagerFactoryBean lcemf = builder
                .dataSource(ds)
                .packages(Classes.class)
                .persistenceUnit("secondPersistenceUnit")
                .properties(createEntityManagerProperties())
                .build();
        return lcemf;
    }

    @Bean(name = "secondEntityManager")
    public EntityManager secondEntityManager(
            @Qualifier("secondEmFactory") LocalContainerEntityManagerFactoryBean le) {
        return le.getObject().createEntityManager();
    }

    @Bean(name = "secondTransaction")
    public PlatformTransactionManager secondTransactionManager(
            @Qualifier("secondEmFactory") LocalContainerEntityManagerFactoryBean lc,
            @Qualifier("secondDataSource") DataSource ds) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(lc.getObject());
        txManager.setDataSource(ds);
        return txManager;
    }


    private Map<String, Object>  createEntityManagerProperties(){
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.ejb.naming_strategy", org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.class.getName());
        properties.put("hibernate.dialect", org.hibernate.dialect.MySQLDialect.class.getName());
    //        properties.put("hibernate.hbm2ddl.auto", "validate");
        properties.put("hibernate.jdbc.use_get_generated_keys", true);
        properties.put("hibernate.id.new_generator_mappings", true);
        properties.put("hibernate.generate_statistics", false);
        properties.put("hibernate.format_sql", true);
        properties.put("hibernate.ddl-auto", false);
        properties.put("hibernate.naming.physical-strategy", org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.class.getName());
        properties.put("hibernate.show_sql", true);
        return properties;
    }

如何使用上面的hibernate

到这里很简单了, 只要在需要事务的地方用 @Transactional(value = "XXXTransaction"), 在需要 EntityManager的地方使用 @PersistenceContext(unitName = "xxxPersistenceUnit", name = "xxxEntityManager")

项目代码


    @Service
    @Transactional(value = "primaryTransaction")
    public class AddressService {

    @PersistenceContext(unitName = "primaryPersistenceUnit" , name = "primaryEntityManager")
    private EntityManager em;

    public Province getProvinceById(Integer id) {

        Province p = em.createQuery("SELECT p FROM Province p WHERE p.id = :id", Province.class)
                .setParameter("id", id)
                .getResultList().stream().findFirst().orElse(null);
        return p;
        }
     }

    @Service
    @Transactional(value = "secondTransaction")
    public class ClassesService {

    @PersistenceContext(unitName = "secondPersistenceUnit", name = "secondEntityManager")
    private EntityManager em;


    public Classes getClassesById(Integer id) {
        return em.createQuery("select c FROM Classes c where c.id = :id", Classes.class)
                .setParameter("id", id)
                .getResultList().stream().findFirst().orElse(null);
    }
}
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
慷慨打赏