簡體   English   中英

Spring 使用 SpringPhysicalNamingStrategy 引導多個數據源

[英]Spring Boot multiple data source with SpringPhysicalNamingStrategy

要求 Spring 啟動應用程序配置了多個數據源。 多數據源配置使用單獨的datasourceentityManagertransactionManager

此外,hibernate 命名配置正在使用具有以下配置的單個數據源。 spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

上述配置可以通過 JPA 屬性提供給 entityManager。 但無法在 SpringPhysicalNamingStrategy 中添加動態表名(來自 application.properties)。

我創建了一個自定義的物理命名策略。 它可以使用環境變量更改名稱。 此外,它也可以更改列名。 名稱標識符將由數據庫目錄名稱決定。 您可以使用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_

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM