首頁 > 軟體

jpa多資料來源時Hibernate設定自動生成表不生效的解決

2022-02-25 10:01:20

jpa設定多資料來源教學很多,在Springboot2之後有一些變化,來看一下。

application.yml如下

spring:
  application:
    name: t3cc
  datasource:
    primary:
        jdbc-url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${DB_NAME:anbang}?useUnicode=true&characterEncoding=UTF8&serverTimezone=Hongkong
        username: root
        password: root
    secondary:
        jdbc-url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${DB_NAME:anbang1}?useUnicode=true&characterEncoding=UTF8&serverTimezone=Hongkong
        username: root
        password: root
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect  #不加這句則預設為myisam引擎
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
    open-in-view: true
    properties:
        enable_lazy_load_no_trans: true
    show-sql: true
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_SERVER:localhost:8848}
###############################---log---##############################
logging:
  file: ./logback.log

yml裡設定了兩個資料來源,和一些jpa和Hibernate的設定。

下面是DataSource的設定

/**
 * @author wuweifeng wrote on 2019/3/5.
 */
@Configuration
public class DataSourceConfig { 
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource dataSourceOrder() {
        return DataSourceBuilder.create().build();
    }
 
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource dataSourceAuth() {
        return DataSourceBuilder.create().build();
    } 
}

下面是第一個資料來源的設定

package com.mm.dmp.t3cc.config; 
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
 
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
 
/**
 * @author wuweifeng wrote on 2019/3/5.
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactoryPrimary",
        transactionManagerRef = "transactionManagerPrimary",
        basePackages = {"com.mm.dmp.t3cc.core.repository.one"})
public class OneConfig {
 
    @Resource
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;
 
    @Primary
    @Bean(name = "entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }
 
    @Resource
    private JpaProperties jpaProperties;
 
    @Resource
    private HibernateProperties properties;
 
    /**
     * 設定實體類所在位置
     */
    @Primary
    @Bean(name = "entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) { 
        LocalContainerEntityManagerFactoryBean entityManagerFactory = builder
                .dataSource(primaryDataSource)
                //.packages(classes)
                //設定實體類所在位置
                .packages("com.mm.dmp.t3cc.core.entity.one")
                .persistenceUnit("primaryPersistenceUnit")
                //.properties(jpaProperties.getProperties())
                .properties(properties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings()))
                .build();
        return entityManagerFactory;
    }
 
    @Primary
    @Bean(name = "transactionManagerPrimary")
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    } 
}

這裡會和別人的設定不一樣,主要區別在於HibernateProperties。別人的在第61行,我註釋掉的那行,會直接使用jpaProperties.getProperties()。當你這樣寫之後,會發現yml裡設定的Hibernate的update自動生成表,和命名方式並沒有生效。

原因我們可以看一下。

這裡就是jpaProperties.getProperties()的地方,如果打斷點可以看到,只有箭頭這一個設定被載入進去了。上面的Hibernate的ddl和naming並沒有進去。

來看一下HibernateProperties

這裡才是真正讓自動建表生效的地方,然而並沒有載入進去。那麼就需要我們手工來新增了。

這裡面有個determineHibernateProperties方法,就是來組合jpaProperties和HibernateProperties的地方。我們應該使用這個方法來組合整個設定的map物件。

也就是在OneConfig類中,把兩個Properties都定義出來,然後組合一下,就是箭頭的地方。在debug時,就可以看到Hibernate的設定也都載入進來了。

OK,以上就是動態資料來源設定Hibernate自動建表不生效的原因。

下面是第二個資料來源的設定

package com.mm.dmp.t3cc.config; 
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
 
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
 
/**
 * @author wuweifeng wrote on 2019/3/5.
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactorySecondary",
        transactionManagerRef = "transactionManagerSecondary",
        basePackages = {"com.mm.dmp.t3cc.core.repository.two"}) //設定Repository所在位置
public class TwoConfig {
 
    @Resource
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;
 
    @Resource
    private JpaProperties jpaProperties;
 
    @Resource
    private HibernateProperties properties;
 
    @Bean(name = "entityManagerSecondary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactorySecondary(builder).getObject().createEntityManager();
    }
 
    @Bean(name = "entityManagerFactorySecondary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean entityManagerFactory
                = builder
                .dataSource(secondaryDataSource)
                //.packages(classes)
                //設定實體類所在位置
                .packages("com.mm.dmp.t3cc.core.entity.two")
                .persistenceUnit("secondaryPersistenceUnit")
                .properties(properties.determineHibernateProperties(jpaProperties.getProperties(), new
                        HibernateSettings()))
                .build();
        return entityManagerFactory;
    }
 
    @Bean(name = "transactionManagerSecondary")
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
    }
}

如果你覺得這樣比較麻煩,並且還有分庫分表的需求,那麼可以使用sharding jdbc來操作,移步這一篇文章

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com