3

I have a spring-boot application with Hibernate. I want to provide custom NamingStrategy to add prefix to every table managed by hibernate.

I found I can use property:

spring.jpa.hibernate.naming_strategy=com.whatever.MyNamingStrategy

This works fine except I want to have the prefix dynamic based on properties. My goal would be to have something like:

@Component
public class PrefixNamingStrategy extends DefaultNamingStrategy {

    private final String prefix;

    @Autowired
    public PrefixNamingStrategy(@Value("db.table.prefix") String prefix) {
        this.prefix = prefix;
    }

    @Override
    public String tableName(String tableName) {
        return prefix + super.tableName(tableName);
    }
}

Obviously this doesn't work with the property.

I tried to provide custom SessionFactoryand provide the NamingStrategy there but had no luck - the bean was created after hibernate initialized and even doesn't seem to be used:

@Autowired
@Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) {

    LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);

    sessionBuilder.setNamingStrategy(...); // !

    return sessionBuilder.buildSessionFactory();
}

Is there a way how to provide custom NamingStrategy as bean?

Cheers

Jan Zyka
  • 17,460
  • 16
  • 70
  • 118

1 Answers1

0

Well knowing this is less than ideal I ended up going through VendorAdapter:

  1. The naming strategy delegator (note NamingStrategy has been deprecated):

    @Component
    public class PrefixNamingStrategyDelegator extends ImprovedNamingStrategyDelegator {
    
    @Autowired
    public PrefixNamingStrategyDelegator(final @Value("${application.environment}_") String prefix) {
        super(
                new HbmNamingStrategyDelegate() {
                    @Override
                    public String toPhysicalTableName(String tableName) {
                        return prefix + super.toPhysicalTableName(tableName);
                    }
                },
                new JpaNamingStrategyDelegate() {
                    @Override
                    public String toPhysicalTableName(String tableName) {
                        return prefix + super.toPhysicalTableName(tableName);
                    }
                }
        );
    }
    
    @Override
    public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm) {
        return super.getNamingStrategyDelegate(isHbm);
    }
    }
    
  2. The JpaPersistenceProvider to sneak the NamingStrategyDelegator into it:

    @Component
    /**
     * See {@link org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider}
     *
     * We can't subclass that class but still need to sneak {@link org.hibernate.cfg.naming.NamingStrategyDelegator} into it.
     */
    public class SpringHibernateJpaPersistenceProviderWithNamingStrategy extends HibernatePersistenceProvider {
    
    private final NamingStrategyDelegator namingStrategy;
    
    @Autowired
    public SpringHibernateJpaPersistenceProviderWithNamingStrategy(NamingStrategyDelegator namingStrategy) {
        this.namingStrategy = namingStrategy;
    }
    
    @Override
    public EntityManagerFactory createContainerEntityManagerFactory(final PersistenceUnitInfo info, Map properties) {
        return new EntityManagerFactoryBuilderImpl(new PersistenceUnitInfoDescriptor(info), properties) {
            @Override
            public Configuration buildHibernateConfiguration(ServiceRegistry serviceRegistry) {
                Configuration configuration = super.buildHibernateConfiguration(serviceRegistry);
                if (info instanceof SmartPersistenceUnitInfo) {
                    for (String managedPackage : ((SmartPersistenceUnitInfo) info).getManagedPackages()) {
                        configuration.addPackage(managedPackage);
                    }
                }
    
                configuration.setNamingStrategyDelegator(namingStrategy);
    
                return configuration;
            }
        }.build();
    }
    }
    
  3. In my @Configuration:

    @Bean
    public AbstractJpaVendorAdapter customJPAVendorAdapter(HibernatePersistenceProvider jpaPersistenceProvider) {
        return new HibernateJpaVendorAdapter() {
            @Override
            public PersistenceProvider getPersistenceProvider() {
                return jpaPersistenceProvider;
            }
        };
    }
    

The PersistenceProvider itself is raw copy (its package private and can't be sub classed) of SpringHibernateJpaPersistenceProvider with added line:

configuration.setNamingStrategyDelegator(namingStrategy);

Works. But ugly as ... (you know)

Jan Zyka
  • 17,460
  • 16
  • 70
  • 118