简体   繁体   English

在 spring boot 中动态设置 hibernate.dialect 属性

[英]Dynamically set hibernate.dialect properties in spring boot

I have gone through available example and tutorials on how to set hibernate.dialect property correctly but found no approach suitable for my situation.我已经浏览了有关如何正确设置hibernate.dialect属性的可用示例和教程,但没有发现适合我的情况的方法。

This tutorial is working best for me, but it lacks the ability to set hibernate.dialect property dynamically as I have different types of databases to connect to: 教程最适合我,但它缺乏动态设置hibernate.dialect属性的能力,因为我有不同类型的数据库要连接:

  • MS SQL微软SQL
  • Oracle甲骨文
  • H2 H2
  • MySQL MySQL

With incorrect dialect, my JPA (delete/update) queries fail.由于方言不正确,我的 JPA(删除/更新)查询失败。

With below implementation of @Configuration , which works perfectly, how may I be able to set hibernate.dialect dynamically at runtime for each datasource?使用以下实现完美的@Configuration ,我如何能够在运行时为每个数据源动态设置hibernate.dialect

Thank you in advance.先感谢您。

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.example.multidb",
        entityManagerFactoryRef = "multiEntityManager",
        transactionManagerRef = "multiTransactionManager"
)
public class PersistenceConfiguration {

    private final String PACKAGE_SCAN = "com.example.multidb";    

    @Primary
    @Bean(name = "mainDataSource")
    @ConfigurationProperties("app.datasource.main")
    public DataSource mainDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }    

    @Bean(name = "clientADataSource")
    @ConfigurationProperties("app.datasource.clienta")
    public DataSource clientADataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "clientBDataSource")
    @ConfigurationProperties("app.datasource.clientb")
    public DataSource clientBDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "multiRoutingDataSource")
    public DataSource multiRoutingDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();

        targetDataSources.put(DBTypeEnum.MAIN, mainDataSource());
        targetDataSources.put(DBTypeEnum.CLIENT_A, clientADataSource());
        targetDataSources.put(DBTypeEnum.CLIENT_B, clientBDataSource());

        MultiRoutingDataSource multiRoutingDataSource = new MultiRoutingDataSource();

        multiRoutingDataSource.setDefaultTargetDataSource(mainDataSource());
        multiRoutingDataSource.setTargetDataSources(targetDataSources);        

        return multiRoutingDataSource;
    }   

    @Bean(name = "multiEntityManager")
    public LocalContainerEntityManagerFactoryBean multiEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(multiRoutingDataSource());
        em.setPackagesToScan(PACKAGE_SCAN);

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();

        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibernateProperties());        

        return em;
    }

    @Bean(name = "multiTransactionManager")
    public PlatformTransactionManager multiTransactionManager() {
        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                multiEntityManager().getObject());        

        return transactionManager;
    }

    @Primary
    @Bean(name = "dbSessionFactory")
    public LocalSessionFactoryBean dbSessionFactory() {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();

        sessionFactoryBean.setDataSource(multiRoutingDataSource());
        sessionFactoryBean.setPackagesToScan(PACKAGE_SCAN);
        sessionFactoryBean.setHibernateProperties(hibernateProperties());

        return sessionFactoryBean;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.show_sql", true);
        properties.put("hibernate.format_sql", true);        

        //set hibernate.dialect for each datasource

        return properties;
    }
}

I've created a working example for you, I'll describe it here but if you want to jump in the code yourself it's available at this GitHub repo .我已经为您创建了一个工作示例,我将在此处对其进行描述,但是如果您想自己跳入代码, 则可以在此 GitHub 存储库中找到它

In my case, I've created two data sources, one for User and another one for Item .就我而言,我创建了两个数据源,一个用于User ,另一个用于Item

Here the entities:这里的实体:

package com.marcosbarbero.so.multiple.ds.entity.user;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@Entity
@Table(schema = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @Column(unique = true, nullable = false)
    private String email;

    private int age;
}

package com.marcosbarbero.so.multiple.ds.entity.item;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@Entity
@Table(schema = "item")
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;
}

Note, It's important to have distinct packages for each domain.请注意,为每个域设置不同的包很重要。 Then I created the Repositories然后我创建了Repositories

package com.marcosbarbero.so.multiple.ds.repository.user;

import com.marcosbarbero.so.multiple.ds.entity.user.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Integer> {
}

package com.marcosbarbero.so.multiple.ds.repository.item;

import com.marcosbarbero.so.multiple.ds.entity.item.Item;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ItemRepository extends JpaRepository<Item, Integer> {
}

Nothing special about the repos.回购没有什么特别之处。 Let's move to the final piece, the configuration.让我们转到最后一部分,即配置。

  1. I created a @ConfigurationProperties class to externalize my configuration, bear with me, I know the naming is not the best :)我创建了一个@ConfigurationProperties类来外部化我的配置,请耐心@ConfigurationProperties ,我知道命名不是最好的:)
package com.marcosbarbero.so.multiple.ds.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Data
@Component
@ConfigurationProperties(prefix = "multi-datasource")
public class MultipleDataSourceProperties {

    private UserDataSourceProperties user = new UserDataSourceProperties();

    private ItemDataSourceProperties item = new ItemDataSourceProperties();

    @Data
    public static class UserDataSourceProperties {
        private HibernateProperties hibernate = new HibernateProperties();
    }

    @Data
    public static class ItemDataSourceProperties {
        private HibernateProperties hibernate = new HibernateProperties();
    }

    @Data
    public static class HibernateProperties {
        private Map<String, String> properties = new HashMap<>();
    }

}

We'll see the properties configuration file soon.我们很快就会看到属性配置文件。

  1. Now let's create the DataSource for the User :现在让我们为User创建DataSource
package com.marcosbarbero.so.multiple.ds.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.marcosbarbero.so.multiple.ds.repository.user",
        entityManagerFactoryRef = "userEntityManager",
        transactionManagerRef = "userTransactionManager"
)
public class UserDataSourceConfig {

    private final MultipleDataSourceProperties properties;

    public UserDataSourceConfig(MultipleDataSourceProperties properties) {
        this.properties = properties;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean userEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(userDataSource());
        em.setPackagesToScan("com.marcosbarbero.so.multiple.ds.entity.user");

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaPropertyMap(properties.getUser().getHibernate().getProperties());

        return em;
    }

    @Primary
    @Bean
    @ConfigurationProperties("multi-datasource.user")
    public DataSource userDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager userTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(userEntityManager().getObject());
        return transactionManager;
    }
}

The important part for you at this class is the line em.setJpaPropertyMap(properties.getUser().getHibernate().getProperties());在这门课上对你来说重要的部分是em.setJpaPropertyMap(properties.getUser().getHibernate().getProperties()); it's getting the User's Hibernate configuration properties from our @ConfigurationProperties class defined above.它从上面定义的@ConfigurationProperties类中获取用户的 Hibernate 配置属性。

  1. Now let's do the same for the Item :现在让我们对Item做同样的事情:
package com.marcosbarbero.so.multiple.ds.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.marcosbarbero.so.multiple.ds.repository.item",
        entityManagerFactoryRef = "itemEntityManager",
        transactionManagerRef = "itemTransactionManager"
)
public class ItemDataSourceConfig {

    private final MultipleDataSourceProperties properties;

    public ItemDataSourceConfig(MultipleDataSourceProperties properties) {
        this.properties = properties;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean itemEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(itemDataSource());
        em.setPackagesToScan("com.marcosbarbero.so.multiple.ds.entity.item");

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaPropertyMap(properties.getItem().getHibernate().getProperties());

        return em;
    }

    @Primary
    @Bean
    @ConfigurationProperties("multi-datasource.item")
    public DataSource itemDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager itemTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(itemEntityManager().getObject());
        return transactionManager;
    }
}
  1. The application.properties application.properties
multi-datasource.item.jdbcUrl=jdbc:h2:mem:spring_jpa_item;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS ITEM
multi-datasource.item.username=sa
multi-datasource.item.password=sa

multi-datasource.item.hibernate.properties.hibernate.hbm2ddl.auto=create-drop
multi-datasource.item.hibernate.properties.hibernate.cache.use_second_level_cache=false
multi-datasource.item.hibernate.properties.hibernate.cache.use_query_cache=false
multi-datasource.item.hibernate.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

multi-datasource.user.jdbcUrl=jdbc:h2:mem:spring_jpa_user;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS USER
multi-datasource.user.username=sa
multi-datasource.user.password=sa

multi-datasource.user.hibernate.properties.hibernate.hbm2ddl.auto=create-drop
multi-datasource.user.hibernate.properties.hibernate.cache.use_second_level_cache=false
multi-datasource.user.hibernate.properties.hibernate.cache.use_query_cache=false
multi-datasource.user.hibernate.properties.hibernate.dialect=org.hibernate.dialect.OracleDialect
  1. Some unit tests一些单元测试
package com.marcosbarbero.so;

import com.marcosbarbero.so.multiple.ds.entity.item.Item;
import com.marcosbarbero.so.multiple.ds.entity.user.User;
import com.marcosbarbero.so.multiple.ds.repository.item.ItemRepository;
import com.marcosbarbero.so.multiple.ds.repository.user.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest
public class JPAMultipleDBTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ItemRepository itemRepository;

    @Test
    public void whenCreatingUser_thenCreated() {
        User user = new User();
        user.setName("John");
        user.setEmail("john@test.com");
        user.setAge(20);
        user = userRepository.save(user);

        assertNotNull(userRepository.findById(user.getId()));
    }

    @Test
    public void whenCreatingProduct_thenCreated() {
        Item item = new Item();
        item.setName("Book");
        item.setId(2);
        item = itemRepository.save(item);

        assertNotNull(itemRepository.findById(item.getId()));
    }
}

I think it also worth mentioning, to make it all work I disabled the DataSourceAutoConfiguration , it's simple as that:我认为还值得一提的是,为了使其一切正常,我禁用了DataSourceAutoConfiguration ,就这么简单:

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

And again, it all is available at this GitHub repo .同样,这一切都可以在这个 GitHub repo 中找到

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果未使用spring-boot设置“ hibernate.dialect”,则对DialectResolutionInfo的访问不能为空 - Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set using spring-boot Spring Boot JPA - 未设置“hibernate.dialect”时,对 DialectResolutionInfo 的访问不能为空 - Spring Boot JPA - Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set HSQL 版本和 Spring 引导 2.72:HibernateException:访问 DialectResolutionInfo 不能是 null 时未设置“hibernate.dialect” - HSQL Version and Spring Boot 2.72: HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set Hibernate:没有设置方言。 设置属性hibernate.dialect - Hibernate: The dialect was not set. Set the property hibernate.dialect 为什么我们设置属性Hibernate.Dialect? - Why we set property Hibernate.Dialect? Spring 引导 JPA - 当“hibernate.dialect”在 JAVA 8u291 中不起作用时,对 DialectResolutionInfo 的访问不能为 null - Spring Boot JPA - Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not working in JAVA 8u291 Spring和Tomcat:未设置&#39;hibernate.dialect&#39;时对DialectResolutionInfo的访问不能为空 - Spring and Tomcat: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set Spring 实体管理器无法启动:当未设置“hibernate.dialect”时,无法访问 DialectResolutionInfo null - Spring Entity Manager cannot be initiated : Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set Hibernate 4未设置&#39;hibernate.dialect&#39;时,对DialectResolutionInfo的访问不能为空 - Hibernate 4 Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set HibernateException:未设置“hibernate.dialect”时,对 DialectResolutionInfo 的访问不能为空 - HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM