genki98
genki98

Reputation: 780

EntityManager doesn't translate camel case to snake case

I'm using SpringBoot 2.7.0 and trying to set an entity manager cause I have 2 databases.

But the entityManager that I instantiated doesn't work like default one.

  1. It doesn't translate camel case to snake case for the properties of entity when it creates tables.
  2. Even it doesn't follow the settings in application.yml. for example, spring.jpa.show-sql.

I configured it with below code.

@Slf4j
@RequiredArgsConstructor
@EnableJpaAuditing
@EnableJpaRepositories(basePackages = "com.xxx.yyy", entityManagerFactoryRef = "businessEntityManagerFactory", transactionManagerRef = "businessTransactionManager")
@EntityScan(basePackages = "com.xxx.yyy")
@Configuration
public class JpaConfiguration {

    @Bean
    public LocalContainerEntityManagerFactoryBean businessEntityManagerFactory(EntityManagerFactoryBuilder builder,
        DataSource businessDataSource) {
        return builder
            .dataSource(businessDataSource)
            .packages("com.xxx.yyy")
            .build();
    }

    @Bean
    public PlatformTransactionManager businessTransactionManager(LocalContainerEntityManagerFactoryBean businessEntityManagerFactory) {
        return new JpaTransactionManager(Objects.requireNonNull(businessEntityManagerFactory.getObject()));
    }
}

Does anyone know how I can instantiate an entity manager with same settings like spring boot default one?

Upvotes: 1

Views: 4130

Answers (2)

genki98
genki98

Reputation: 780

Sven's answer might be right but I didn't try it because I saw this after I found the solution and it looked requiring many changes my codes.

I figured this out with below codes

@Bean
public LocalContainerEntityManagerFactoryBean businessEntityManagerFactory(DataSource businessDataSource) {
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setDataSource(businessDataSource);
    emf.setPackagesToScan("com.xxx.yyy");

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setShowSql(Boolean.valueOf(env.getProperty("spring.jpa.show-sql")));
    emf.setJpaVendorAdapter(vendorAdapter);

    HashMap<String, Object> properties = new HashMap<>();
    properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
    properties.put("hibernate.format_sql", env.getProperty("spring.jpa.properties.hibernate.format_sql"));
    properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.properties.hibernate.hbm2ddl.auto"));
    properties.put("hibernate.physical_naming_strategy", "org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
    properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
    emf.setJpaPropertyMap(properties);

    return emf;
}

Upvotes: 2

Sven D&#246;ring
Sven D&#246;ring

Reputation: 4368

Spring's convention-over-configuration does not work well with multiple database configurations.

Particuallary the configuration has to be autowired "manually". That's the reason springt.jpa.show-sql is not considered.

I will post an example on how to configure a database "manually".

import java.util.Map;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariDataSource;

@Configuration
@EnableTransactionManagement
@EnableAutoConfiguration(exclude = {JdbcRepositoriesAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class, DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableJpaRepositories(basePackageClasses = {ExampleXyzRepository.class}, transactionManagerRef = DatabaseConfig.SPRING_TRANSACTION_MANAGER)
public class DatabaseConfig {
    public static final String SPRING_TRANSACTION_MANAGER = "springTransactionManager";

    private static LocalContainerEntityManagerFactoryBean createEntityManagerFactory(
            final ObjectProvider<PersistenceUnitManager> persistenceUnitManagerProvider, final JpaProperties jpaProperties,
            final HibernateProperties hibernateProperties, final DataSource dataSource, final Class<?> exampleEntityClass, final String persistenceUnit) {
        return createEntityManagerFactoryBuilder(persistenceUnitManagerProvider.getIfAvailable(), jpaProperties, hibernateProperties)
                .dataSource(dataSource)
                .packages(exampleEntityClass)
                .persistenceUnit(persistenceUnit)
                .build();
    }

    private static EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(
            final PersistenceUnitManager persistenceUnitManager, final JpaProperties jpaProperties, final HibernateProperties hibernateProperties) {
        final JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
        final Map<String, Object> expandedProperties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
        return new EntityManagerFactoryBuilder(jpaVendorAdapter, expandedProperties, persistenceUnitManager);
    }

    private static JpaVendorAdapter createJpaVendorAdapter(final JpaProperties jpaProperties) {
        final AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setShowSql(jpaProperties.isShowSql());
        adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
        adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
        if (jpaProperties.getDatabase() != null) {
            adapter.setDatabase(jpaProperties.getDatabase());
        }
        return adapter;
    }

    private static HikariDataSource initializeDataSource(final DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource")
    public DataSourceProperties springDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public DataSource springDataSource(@Qualifier("springDataSourceProperties") final DataSourceProperties springDataSourceProperties) {
        return initializeDataSource(springDataSourceProperties);
    }

    @Bean
    @ConfigurationProperties("spring.jpa")
    public JpaProperties springJpaProperties() {
        return new JpaProperties();
    }

    @Bean
    @ConfigurationProperties("spring.jpa.hibernate")
    public HibernateProperties springHibernateProperties() {
        return new HibernateProperties();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            final ObjectProvider<PersistenceUnitManager> persistenceUnitManagerProvider,
            @Qualifier("springJpaProperties") final JpaProperties springJpaProperties,
            @Qualifier("springHibernateProperties") final HibernateProperties springHibernateProperties,
            @Qualifier("springDataSource") final DataSource springDataSource) {
        return createEntityManagerFactory(persistenceUnitManagerProvider, springJpaProperties, springHibernateProperties, springDataSource, ExampleXyzEntity.class, "default-persistence-unit");
    }

    @Bean(SPRING_TRANSACTION_MANAGER)
    public JpaTransactionManager transactionManager(@Qualifier("entityManagerFactory") final EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

See how the configuration of DataSourceProperties, HibernateProperties, DataSource and JpaProperties are bound.


The second database is configured similar.

But:

  • use a different configuration prefix and not spring.jpa and spring.datasource - obviously
  • define the transactionManager in the org.springframework.transaction.annotation.Transactional while accessing the second database, e.g. in the CrudRepository

Upvotes: 1

Related Questions