Невозможно установить стратегию именования JPA после настройки нескольких источников данных (Spring 1.4.1/Hibernate 5.x)

Я использую Spring Boot 1.4.1, который использует Hibernate 5.0.11. Первоначально я настроил источник данных, используя 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

Я настроил его с помощью "дяди", потому что это будет имя одного из нескольких источников данных, которые я настроил. Я настроил этот источник данных, как это, согласно документам Spring:

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

На данный момент все работает нормально.

Я создал класс @Entity без каких- @Column аннотаций @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

... а также это, следуя документам Spring для настройки нескольких источников данных. По-видимому, как только вы определяете второй источник данных, он не может настроить фасоли по умолчанию, и вы должны определить свой собственный EntityManager и TransactionManager. Поэтому в дополнение к источнику данных, сконфигурированному выше, я добавил следующие конфигурации:

@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();
}

Это работает с точки зрения подключения к базе данных и попыток получения данных.

ОДНАКО (и вот проблема, спасибо за то, что вы читаете это). После этих конфигураций я потерял подразумеваемую стратегию именования, которая переводит имена столбцов 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

Но это не сработало. Я пробовал некоторые варианты, такие как:

spring.datasource.uncle.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy

а также

spring.datasource.uncle.hibernate.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy

но это не имело никакого эффекта.

Затем я прочитал, что в Hibernate 5 стратегия именования была разбита на две части: "физическая" и "неявная", и для каждой из них есть разные настройки. Поэтому я попробовал это, с несколькими вариантами:

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

а также

spring.datasource.uncle.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

а также

spring.datasource.uncle..hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy

Но никто из них не работал.

Похоже, что мне нужно установить эту конфигурацию непосредственно в bean-компонентах, например, в SessionFactory, но я не смог найти этот API. Документация вокруг этого, похоже, имеет некоторые пробелы.

Мне бы очень хотелось избежать установки persistence.xml, который мне не нужен до этого момента.

Так вот, где я застрял, и я надеюсь, что кто-то может помочь. На самом деле, что бы я хотел, это способ отладки этих параметров свойств, я включил ведение журнала трассировки как в org.springframework и в org.hibernate но там ничего не было полезно. Я попытался выполнить код, когда эти компоненты были настроены, но не смог найти место, где это происходит. Если кто-то имеет эту информацию и может поделиться ею, я был бы очень благодарен.

Ответы

Ответ 1

У меня была та же проблема и исправлена ее с помощью следующего кода (адаптированного к коду в вопросе - для одного менеджера сущностей):

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();
}

Ответ 2

Если вы используете SessionFactory, вы должны использовать следующие строки, чтобы установить стратегии именования.

sessionFactory.setImplicitNamingStrategy(SpringImplicitNamingStrategy.INSTANCE);
sessionFactory.setPhysicalNamingStrategy(new SpringPhysicalNamingStrategy());

Ответ 3

То же, что и @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 does not 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

Ответ 4

Единственный способ, которым я правильно запустил это с Spring-Boot 2+, это установить вручную:

  @Bean(name = "myEmf")
  public LocalContainerEntityManagerFactoryBean sapEntityManagerFactory(
      EntityManagerFactoryBuilder builder, @Qualifier("myDataSource") DataSource dataSource) {
    return builder
        .dataSource(dataSource)
        .packages("my.custom.package")
        .persistenceUnit("myPu")
        .properties(getProperties())
        .build();
  }

  public Map<String, String> getProperties() {
    if (environment.getActiveProfiles().length == 1
        && environment.getActiveProfiles()[0].equals("test")) {
      return Collections.singletonMap("hibernate.hbm2ddl.auto", "create");
    }
    return Collections.emptyMap();
  }