Spring Boot 使用 SpringPhysicalNamingStrategy 实现多数据源

2024-03-24

需要配置多个数据源的 Spring boot 应用程序。 多个数据源配置正在使用单独的datasource, entityManager and transactionManager.

此外,休眠命名配置还可以使用具有以下配置的单个数据源。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);
    }
}

Test

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("[email protected] /cdn-cgi/l/email-protection");
        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_
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Boot 使用 SpringPhysicalNamingStrategy 实现多数据源 的相关文章

随机推荐

  • 处理 ASP.NET MVC 控制器构造函数中发生的异常

    处理控制器构造函数内发生的异常的最佳方法是什么 我能想到的就是使用 Application OnError 或在我的 ControllerFactory 中放置一个 try catch 这些解决方案似乎都不理想 Application On
  • node.js 区分发出 http 请求时的错误

    我的node js应用程序正在使用http request到 REST APIhttp army gov launch nukes我需要区分三种可能的情况 Success 服务器回复肯定 我知道我的敌人已经被消灭了 Failure 要么我从
  • 如何将 QGraphicsItem QList 保存到文件然后读取它? Qt C++

    我有一个包含 QGraphicsItem 的列表 这个列表让我可以画线 我查看了文档 但没有看到如何将点保存在文件中 然后读取文件的点才能显示它们 这是我的代码 QList
  • 如何解决 Angular 项目中的 Tailwind 和 Bootstrap 冲突

    我在同一个 Angular 项目中使用 Tailwind CSS 和 Bootstrap ngx bootstrap 大多数情况下 他们相处得很好 然而 当涉及到填充和边距时 它们就像兄弟姐妹一样互相争斗 我想使用 Tailwind 进行填
  • LINQ:通过多个属性将对象列表分组到列表列表中的更好方法?

    我有一个这样的对象列表 var cmps new List
  • 如何加快创建聚集索引的速度

    在 SQL Server 中 如果我想向聚簇索引定义添加一新列 则没有用于更改聚簇索引的选项 唯一的选择是删除并使用新定义创建簇索引 据我了解 对于大容量表来说 删除和创建簇索引是非常昂贵且耗时的 聚集索引创建会重建表上的所有非聚集索引 这
  • SharePoint 列表项权限

    我想以编程方式实现它 以便用户只能看到列表中的特定项目 基本上 在创建项目时运行的工作流程中 我将执行一些操作并通知一些人有关此项目的信息 我还希望它更改项目的权限 以便只有特定用户 根据项目内容在运行时查找 才能读取该项目 有权访问该列表
  • 如何轻松更改PNG图像的颜色? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有代表扑克牌的 PNG 图像 它们是标准颜色 梅花和黑桃是空白 方块和红心是红色 我想通过将梅花转换为绿色 将方块转换为蓝色来创建
  • 使用 php 从给定 URL 下载文件,通过传递用户名和密码进行 http 身份验证

    我需要使用 php 代码下载文本文件 该文件具有 http 身份验证 我应该为此使用什么程序 我应该使用fsocketopen或卷曲或有其他方法可以做到这一点吗 我正在使用 fsocketopen 但它似乎不起作用 fp fsockopen
  • OSX:如何从 IOUSBDeviceInterface 或位置 id 获取卷名称(或 bsd 名称)

    我正在尝试编写一个应用程序 将特定的 USB 字符串描述符 USB 大容量存储设备 与其卷或 bsd 名称相关联 因此 代码会遍历所有连接的 USB 设备 获取字符串描述符并从其中之一提取信息 我想获取这些 USB 设备的卷名 我找不到合适
  • android:noHistory="true" 是如何工作的?

    假设我有一个带有菜单的基本活动 当我单击菜单项 A 时 它会转到活动 A 我再次打开菜单 然后转到 B 从 B 返回到 A 这样来回一会儿 所以堆栈将是 A B A B A B 当我按下后退按钮时 它会按预期向后浏览堆栈 然而 假设我不想要
  • v-bind 错误:v-bind' 是未声明的前缀

    我正在 asp net 中使用 Orckestra CMS 在 Composite 之前 和 Razor 模板 并尝试使用 Vue 框架 使用 option text 时一切都很好
  • Jtable 编辑单元格的外观和感觉

    有一个JTable with DefaultTableModel 有桌子的setforeground setbackground and setselectioncolor方法 另外 当您编辑单元格时 您有table setDefaultE
  • 获取图像的特定部分(图片)

    我想剪切图片的特定部分 并用它来将裁剪后的图像与硬盘中存储的另一图像进行比较 问题是我不知道如何获取源图像的 特定部分 我知道要裁剪的图像的位置 X Y 这将加载原始文件并创建一个从 0 0 开始 尺寸为 64x64 的裁剪版本 Bitma
  • MVVM 中的故事板动画

    我试图淡入然后淡出文本块以在 MVVM 中显示成功消息 但我无法让它再次淡出 我看了这个 WPF MVVM 属性更改动画 https stackoverflow com questions 1649828 wpf mvvm property
  • 错误:LaTeX 中缺少 \begin{document} [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我的 LaTeX 文档中有错误 运行我的文件时出现错误 LaTeX Error Missing begin document 和这个标签
  • 如何跟踪语速

    我正在开发一个跟踪语速的 iPhone 应用程序 并希望使用 Nuance Speechkit https developer nuance com public Help DragonMobileSDKReference iOS Spee
  • 在另一个角上绘制一个图形

    我应该如何在 R 中的另一个图的角落呈现一个小图 我知道这个问题已经结束了 但我要把这个例子留给后代 一旦掌握了基础知识 您就可以使用基本的 网格 包轻松地进行这样的自定义可视化 这是我使用的一些自定义函数的快速示例以及绘制数据的演示 自定
  • ListView 保持选中状态?

    我有一个充满项目的列表视图 在用户选择一个项目后它会亮起 然后恢复正常 有没有办法让用户在 ListView 中选择一个项目时它保持选中状态并突出显示 显然 消失的选择 是有意设计的 这是一种叫做 触摸模式 http developer a
  • Spring Boot 使用 SpringPhysicalNamingStrategy 实现多数据源

    需要配置多个数据源的 Spring boot 应用程序 多个数据源配置正在使用单独的datasource entityManager and transactionManager 此外 休眠命名配置还可以使用具有以下配置的单个数据源 spr