【问题标题】:Spring Boot multiple data source with SpringPhysicalNamingStrategy使用 SpringPhysicalNamingStrategy 的 Spring Boot 多数据源
【发布时间】:2021-01-11 01:33:52
【问题描述】:

需要配置多个数据源的 Spring Boot 应用程序。 多数据源配置使用单独的datasourceentityManagertransactionManager

hibernate 命名配置也适用于具有以下配置的单个数据源。 spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

上述配置可以通过 JPA 属性提供给 entityManager。但无法在 SpringPhysicalNamingStrategy 中添加动态表名(来自 application.properties)。

【问题讨论】:

    标签: spring-boot hibernate multiple-databases naming-strategy


    【解决方案1】:

    我创建了一个自定义物理命名策略。它可以使用环境变量更改名称。此外,它也可以更改列名。名称标识符将由数据库目录名称决定。您可以使用jdbcEnvironment 更改选择标准。如果你把任何标准选项文本将是属性值。

    应用程序属性

    spring.datasource.url=jdbc:h2:mem:testdb
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=password
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
    
    spring.jpa.show-sql=true
    
    spring.jpa.hibernate.naming.physical-strategy=com.example.demo.DynamicPhysicalNamingStrategy
    
    # dynamic
    user.table.name=TESTDB:TestUser,TESTDB2:TestUser2
    user.column.name=username
    

    用户实体

    @Table(name = "${user.table.name}")
    @Entity
    @Data
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private int id;
    
        @Column(name = "${user.column.name}")
        private String name;
    
        private String email;
    }
    

    动态物理命名策略

    package com.example.demo;
    
    import org.hibernate.boot.model.naming.Identifier;
    import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
    import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.core.env.Environment;
    import org.springframework.stereotype.Component;
    import org.springframework.util.Assert;
    
    import java.util.Locale;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * It is copied from {@link org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl}
     */
    @Component
    public class DynamicPhysicalNamingStrategy implements PhysicalNamingStrategy, ApplicationContextAware {
        private final Pattern VALUE_PATTERN = Pattern.compile("^\\$\\{([\\w.]+)}$");
        private Environment environment;
    
        @Override
        public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
            return apply(name, jdbcEnvironment);
        }
    
        @Override
        public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
            return apply(name, jdbcEnvironment);
        }
    
        @Override
        public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
            return apply(name, jdbcEnvironment);
        }
    
        @Override
        public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
            return apply(name, jdbcEnvironment);
        }
    
        @Override
        public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
            return apply(name, jdbcEnvironment);
        }
    
        private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
            if (name == null) {
                return null;
            }
    
            // Custom Implementation Start
            String text = name.getText();
            Matcher matcher = VALUE_PATTERN.matcher(text);
            if (matcher.matches()) {
                String propertyKey = matcher.group(1);
                text = environment.getProperty(propertyKey);
                Assert.notNull(text, "Property is not found '" + propertyKey + "'");
    
                // extract catalog selection part
                // Example:
                // Current Catalog: TESTDB
                // Property: TESTDB:TestUser, TESTDB2:TestUser
                // Text will be TestUser
                Pattern catalogPattern = Pattern.compile(jdbcEnvironment.getCurrentCatalog().getText() + ":([^,]+)");
                Matcher catalogMatcher = catalogPattern.matcher(text);
                if (catalogMatcher.find()) {
                    text = catalogMatcher.group(1);
                }
    
                // Caution: You can remove below return function, if so text will be transformed with spring advice
                return getIdentifier(text, name.isQuoted(), jdbcEnvironment);
            }
            // Custom Implementation End
    
    
            StringBuilder builder = new StringBuilder(text.replace('.', '_'));
            for (int i = 1; i < builder.length() - 1; i++) {
                if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) {
                    builder.insert(i++, '_');
                }
            }
            return getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
        }
    
        /**
         * Get an identifier for the specified details. By default this method will return an
         * identifier with the name adapted based on the result of
         * {@link #isCaseInsensitive(JdbcEnvironment)}
         *
         * @param name            the name of the identifier
         * @param quoted          if the identifier is quoted
         * @param jdbcEnvironment the JDBC environment
         * @return an identifier instance
         */
        protected Identifier getIdentifier(String name, boolean quoted, JdbcEnvironment jdbcEnvironment) {
            if (isCaseInsensitive(jdbcEnvironment)) {
                name = name.toLowerCase(Locale.ROOT);
            }
            return new Identifier(name, quoted);
        }
    
        /**
         * Specify whether the database is case sensitive.
         *
         * @param jdbcEnvironment the JDBC environment which can be used to determine case
         * @return true if the database is case insensitive sensitivity
         */
        protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
            return true;
        }
    
        private boolean isUnderscoreRequired(char before, char current, char after) {
            return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            environment = applicationContext.getBean(Environment.class);
        }
    }
    
    

    测试

    package com.example.demo;
    
    import com.example.demo.entity.User;
    import com.example.demo.repository.UserRepository;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class DemoApplicationTests {
        @Autowired
        private UserRepository userRepository;
    
        @Test
        void contextLoads() {
            User user = new User();
            user.setName("test");
            user.setEmail("test@test.com");
            userRepository.save(user);
    
            userRepository.findAll();
        }
    
    }
    
    Hibernate: call next value for hibernate_sequence
    Hibernate: insert into testuser (email, username, id) values (?, ?, ?)
    Hibernate: select user0_.id as id1_0_, user0_.email as email2_0_, user0_.username as username3_0_ from testuser user0_
    

    【讨论】:

      猜你喜欢
      • 2015-02-21
      • 1970-01-01
      • 2021-03-07
      • 1970-01-01
      • 1970-01-01
      • 2016-09-14
      • 2021-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多