JPA使用多个数据库模式

29

我在使用JPA/Spring时遇到一个问题:

如何为实体动态指定模式?

我们有属于模式AD的TABLE1和属于模式BD的TABLE2。

@Entity
@Table(name = "TABLE1", schema="S1D")
...

@Entity
@Table(name = "TABLE2", schema="S2D")
...

由于环境(开发/验收/生产)的不同,模式不能硬编码在注释属性中。(在验收中,模式为S1A和S2A)

我该如何实现?是否可以指定某种占位符,例如:

@Entity
@Table(name = "TABLE1", schema="${schema1}")
...

@Entity
@Table(name = "TABLE2", schema="${schema2}")
...

如何根据环境中的属性文件替换模式(schemas)?

谢谢

7个回答

10

我遇到了同样的问题,我通过创建一个persistence.xml文件解决了这个问题,在该文件中我引用了所需的orm.xml文件,并声明了数据库模式。

<persistence
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0" >
<persistence-unit name="schemaOne">
    . . .
    <mapping-file>ormOne.xml</mapping-file>
    . . .
</persistence-unit>

<persistence-unit name="schemaTwo">
    . . .
    <mapping-file>ormTwo.xml</mapping-file>
    . . .
 </persistence-unit>
</persistence>

现在,您可以为您的特定模式创建一个EntityManagerFactory。

EntityManagerFactory emf = Persistence.createEntityManagerFactory("schemaOne");

如果OrmOne和OrmTwo之间存在关联,怎么办? - Shaaban Ebrahim

3

如果你在部署时知道了,你可以做的一件事是准备两个orm.xml文件。一个用于schema1,另一个用于schema2,然后在persistence.xml中定义两个persistence-unit。如果需要更改诸如模式之类的东西,放置注释是一种反模式。


3

我在Github上发布了这篇文章,现在也在这里转发一下。

我发现通过覆盖物理命名策略,可以实现多个环境中多个模式的动态模式配置。

假设你有两个像这样的实体,它们被配置为两个不同的模式 -

@Entity
@Table(name="TABLE1", schema="schema1")
public class FooEntity implements Serializable {
    ...
}

@Entity
@Table(name="TABLE2", schema="schema2")
public class BarEntity implements Serializable {
    ...
}

首先在你的application.yml文件中创建一个配置:
multischema:
  config:
    schema1: FIRSTSCHEMA
    schema2: SECONDSCHEMA

将其绑定到 ConfigurationProperties bean

@Component
@ConfigurationProperties("multischema")
public class MultiSchemaConfigurationProperties {

    private Map<String,String> config;

    public void setConfig(Map<String,String> config) {
        this.config = config;
    }

    public Map<String,String> getConfig() {
        return config;
    }

}

创建一个自定义的物理命名策略,该策略注入MultiSchemaConfigurationProperties并扩展您版本的Spring Boot默认设置。在这种情况下,我正在使用Spring Boot 2.6.4,它使用CamelCaseToUnderscoresNamingStrategy。
@Component
public class MultiSchemaPhysicalNamingStrategy extends CamelCaseToUnderscoresNamingStrategy {

    private final MultiSchemaConfigurationProperties schemaConfiguration;

    public MultiSchemaPhysicalNamingStrategy(MultiSchemaConfigurationProperties schemaConfiguration) {
        this.schemaConfiguration = schemaConfiguration;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        if(name != null) {
            Identifier identifier = super.getIdentifier(schemaConfiguration.getConfig()
                    .get(name.getText()), name.isQuoted(), jdbcEnvironment);
            return super.toPhysicalSchemaName(identifier, jdbcEnvironment);
        }
        return name;
    }

}

应用程序启动时,Hibernate将调用自定义物理命名策略,使用application.yml中提供的配置应用物理命名规则来为模式应用命名。 https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.data-access.configure-hibernate-naming-strategy

FooEntity中的"schema1"将被替换为"value firstschema",而BarEntity中的"schema2"将被替换为"value secondschema"。


有人试过这个解决方案吗?它能完全运行吗? - user5163359
我们使用这种方法时没有遇到任何问题。起初,我们尝试创建多个数据源,但这意味着每个模式一个单独的事务管理器。一旦你有了单独的事务管理器,你将遇到其他问题,其中你不能在同一事务中使用多个模式。通过这种方式,我们仍然使用一个数据源/事务管理器,并具有动态配置。 - zistheman12

2
尝试以下操作:
puplic class MyClass {
  public static final String S1D="S1D";
  public static final String S2D="S2D";
}

@Entity
@Table(name = "TABLE1", schema=MyClass.S1D)
...

@Entity
@Table(name = "TABLE2", schema=MyClass.S2D)
...

请问这个解决方案在不同的环境中如何工作,因为每个环境中模式名称都会更改? - Brooklyn99

1

1

您可以在context.xml中声明两个DataSource(每个模式一个),并使用这些数据源定义两个持久化单元。然后,不同环境的应用服务器上的context.xml可以是不同的。


0

注解参数必须是final的,因此不能在运行时更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接