配置多个数据源后无法设置JPA命名策略(Spring 1.4.1/Hibernate 5.x)

Unc*_*air 23 spring hibernate spring-data-jpa spring-boot

我使用的是使用Hibernate 5.0.11的Spring Boot 1.4.1.最初我使用application.properties如下配置数据源:

spring.datasource.uncle.url=jdbc:jtds:sqlserver://hostname:port/db
spring.datasource.uncle.username=user
spring.datasource.uncle.password=password
spring.datasource.uncle.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.datasource.uncle.driverClassName=net.sourceforge.jtds.jdbc.Driver
Run Code Online (Sandbox Code Playgroud)

我用"叔叔"配置它,因为这将是我将配置的多个数据源之一的名称.我根据Spring文档配置了这样的数据源:

@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.uncle")
public DataSource uncleDataSource() {
    return DataSourceBuilder.create().build();
}
Run Code Online (Sandbox Code Playgroud)

在这一点上一切正常.

我创建了一个@Entity没有任何@Column注释的类,让Hibernate找出列名,例如,如果我有一个名为Java的属性idBank,Hibernate将自动假设列名为id_bank.这在生成ddl,运行SQL语句等时使用.我想利用此功能,因为我将拥有大量实体类,并且不希望创建和维护所有@Column注释.在这一点上,这很好.

然后我添加了另一个这样的数据源:

spring.datasource.aunt.url=jdbc:sybase:Tds:host2:port/db2
spring.datasource.aunt.username=user2
spring.datasource.aunt.password=password2
spring.datasource.aunt.dialect=org.hibernate.dialect.SybaseDialect
spring.datasource.aunt.driverClassName=com.sybase.jdbc4.jdbc.SybDriver
Run Code Online (Sandbox Code Playgroud)

......还有这个,遵循Spring文档设置多个数据源.显然,一旦定义了第二个数据源,它就无法配置默认bean,你必须定义自己的EntityManagerTransactionManager.所以除了上面配置的数据源之外,我还添加了以下配置:

@Bean
@Primary
PlatformTransactionManager uncleTransactionManager(@Qualifier("uncleEntityManagerFactory") final EntityManagerFactory factory) {
    return new JpaTransactionManager(factory);
}

@Bean
@Primary
LocalContainerEntityManagerFactoryBean uncleEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(uncleDataSource())
            .packages(Uncle.class)
            .persistenceUnit("uncle")
            .build();
}

@Bean
@ConfigurationProperties(prefix = "spring.datasource.aunt")
public DataSource auntDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean
PlatformTransactionManager auntTransactionManager(@Qualifier("auntEntityManagerFactory") final EntityManagerFactory factory) {
    return new JpaTransactionManager(factory);
}

@Bean
LocalContainerEntityManagerFactoryBean auntEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(auntDataSource())
            .packages(Aunt.class)
            .persistenceUnit("aunt")
            .build();
}
Run Code Online (Sandbox Code Playgroud)

这适用于连接到数据库并尝试获取数据.

然而(这是问题,感谢你阅读这篇文章).在这些配置之后,我已经失去了将Java列名转换为蛇案例名称的隐含命名策略,所以现在如果我有一个Java属性,idBank它会错误地使用列名idBank而不是id_bank.我真的想要恢复这个功能.

有一个JPA属性,spring.jpa.hibernate.naming-strategy在Spring和Hibernate中有各种命名策略类,如org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy.所以我尝试这样设置:

spring.jpa.hibernate.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
Run Code Online (Sandbox Code Playgroud)

但它没有用.我尝试了一些变化,例如:

spring.datasource.uncle.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
Run Code Online (Sandbox Code Playgroud)

spring.datasource.uncle.hibernate.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
Run Code Online (Sandbox Code Playgroud)

但这没有任何效果.

然后我读到在Hibernate 5中,命名策略分为两部分,"物理"和"隐式",每种都有不同的设置.所以我尝试了这个,有一些变化:

spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
Run Code Online (Sandbox Code Playgroud)

spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
Run Code Online (Sandbox Code Playgroud)

spring.datasource.uncle.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
Run Code Online (Sandbox Code Playgroud)

spring.datasource.uncle..hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
Run Code Online (Sandbox Code Playgroud)

但这些都没有奏效.

似乎应该有一种方法让我直接在bean中设置这个配置,比如在SessionFactory,但是我找不到那个API.围绕这个的文档似乎有一些差距.

我真的很想避免设置一个persistence.xml我也不需要的东西.

所以这就是我被困住的地方,我希望有人可以提供帮助.真的我想要的是一种调试这些属性设置的方法,我在两者中启用了跟踪日志记录org.springframework,org.hibernate但那里没有什么用处.我尝试在配置这些bean时单步执行代码,但找不到发生这种情况的地方.如果有人有这些信息并且可以分享它,我会非常感激.

ewe*_*ert 49

我有同样的问题并使用以下代码修复它(适用于问题中的代码 - 对于单个实体管理器):

protected Map<String, Object> jpaProperties() {
    Map<String, Object> props = new HashMap<>();
    props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
    props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
    return props;
}

@Primary
@Bean(name = "defaultEntityManager")
public LocalContainerEntityManagerFactoryBean defaultEntityManagerFactory(
    EntityManagerFactoryBuilder builder) {
    return builder
        .dataSource(auntDataSource())
        .packages(Aunt.class)
        .persistenceUnit("aunt")
        .properties(jpaProperties())
        .build();
}
Run Code Online (Sandbox Code Playgroud)

  • 我正在使用调试器并在MetadataBuilderImpl中看到了休眠读取的属性的名称.有时这是唯一剩下的解决方案,如果一个人绝对感到沮丧,因为所需的信息似乎不可用.;-)也许通过在EntityManagerFactory上使用@ConfigurationProperties注释可以更加简化,就像在DataSource上完成一样.但它对我没有用. (3认同)
  • 谢谢你,这成功了!我能问一下你是怎么想出来的吗?我花了很多时间浏览文档、源代码并查看示例。我不知道如何调试这些属性,也不知道 Spring 和 Hibernate 期望它们是什么名称。 (2认同)
  • 弃用 `SpringPhysicalNamingStrategy`,改用 `CamelCaseToUnderscoresNamingStrategy`。[GitHub](https://github.com/spring-projects/spring-boot/issues/27352) (2认同)

mar*_*osh 15

可以使用属性获得与@ewert 相同的答案:

# this works
spring.jpa.properties.hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
spring.jpa.properties.hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

# but that doesn't work
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
Run Code Online (Sandbox Code Playgroud)


Ale*_*ppa 6

根据您最新的问题,我想我可以解释为什么默认行为消失。

从 Spring Boot 2.4.2 开始,默认配置通过以下方法启动JpaBaseConfiguration

    @Bean
    @Primary
    @ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder) {
        Map<String, Object> vendorProperties = getVendorProperties();
        customizeVendorProperties(vendorProperties);
        return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan()).properties(vendorProperties)
                .mappingResources(getMappingResources()).jta(isJta()).build();
    }
Run Code Online (Sandbox Code Playgroud)

它发生在customizeVendorProperties方法调用中。

通过创建您自己的LocalContainerEntityManagerFactoryBeanbean(实际上是两个),就不再执行此定制。