How should I register custom Hibernate 5 data type (BasicType) when Spring Data is used?

此生再无相见时 提交于 2020-06-24 11:48:07

问题


I use Spring Data and decided that I want to create new custom data type that can be used in Hibernate entities. I checked the documentation and choose BasicType and implemented it according to this official user guide.

I wanted to be able to register the type under its class name and be able to use the new type in entities without need for @Type annotation. Unfortunately, I’m unable to get reference to the MetadataBuilder or Hibernate configuration to register the new type. Is there a way how to get it in Spring Data? It seems that initialization of the Hibernate is hidden from the user and cannot be easily accessed. We use following class to initialize the JPA:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactory",
        transactionManagerRef = "transactionManager",
        basePackages = {
                "..." // omitted
        }
)
public class JpaConfiguration implements TransactionManagementConfigurer {

    @Primary
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory(
        DataSource dataSource,
        SchemaPerTenantConnectionProviderImpl provider) {

        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setPersistenceUnitName("defaultPersistenceUnit");
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setPackagesToScan(
                "..." // omitted
        );
        entityManagerFactoryBean.setJpaProperties(properties(provider));
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        return entityManagerFactoryBean;
    }

    @Primary
    @Bean(name = "transactionManager")
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new JpaTransactionManager();
    }

    private Properties properties(SchemaPerTenantConnectionProviderImpl provider) {
        Properties properties = new Properties();
        // omitted
        return properties;
    }
}

I found lots of articles about way how to do it with Hibernate’s Configuration object but this one refers to Hibernate 3 and 4. I also found way how to do it via Hibernate org.hibernate.integrator.spi.Integrator but when I use it according to the articles I found I will get exception with the message “org.hibernate.HibernateException: Can not alter TypeRegistry at this time” What is the correct way to register custom types in Spring Data?


回答1:


I finally figured it out. I will post it here for others:

I created a new class that implements org.hibernate.boot.spi.SessionFactoryBuilderFactory interface. In this class I can get reference to the TypeResolver from metadata and register my custom type.

package com.example.configuration;

import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryBuilderFactory;
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
import org.slf4j.LoggerFactory;

import com.example.CustomType;

public class CustomDataTypesRegistration implements SessionFactoryBuilderFactory {

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomDataTypesRegistration.class);

    @Override
    public SessionFactoryBuilder getSessionFactoryBuilder(final MetadataImplementor metadata, final SessionFactoryBuilderImplementor defaultBuilder) {
        logger.info("Registering custom Hibernate data types");
        metadata.getTypeResolver().registerTypeOverride(CustomType.INSTANCE);
        return defaultBuilder;
    }
}

The class must be then registered via Java ServiceLoader mechanism by adding full name of the class with its packages into the file with name org.hibernate.boot.spi.SessionFactoryBuilderFactory into the java module’s META-INF/services directory:

src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory

The file can contain multiple lines, each referencing different class. In this case it is:

com.example.configuration.CustomDataTypesRegistration 

This way the Spring Data starts and custom type is successfully registered during Hibernate initialization.

What helped my quite a lot was this SO answer that deals with schema export in Hibernate 5 under Spring Data.




回答2:


There's a much easier solution to this -- in fact, it's just 1 line of code. You can just use the @TypeDef annotation and thus avoid having to register the custom type:

@Entity(name = "Product")
@TypeDef(
    name = "bitset",
    defaultForType = BitSet.class,
    typeClass = BitSetType.class
)
public static class Product {

    @Id
    private Integer id;

    private BitSet bitSet;

For an example, see "Example 11. Using @TypeDef to register a custom Type" in http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html




回答3:


I use JPA with Spring 4.3.9 and Hibernate 5.0.5 and I use custom property EntityManagerFactoryBuilderImpl.TYPE_CONTRIBUTORS with Spring LocalContainerEntityManagerFactoryBean to override Hibernate BasicTypes.

final Properties jpaProperties = new Properties();
jpaProperties.put(EntityManagerFactoryBuilderImpl.TYPE_CONTRIBUTORS, new TypeContributorList() {
    @Override
    public List<TypeContributor> getTypeContributors() {
         return Lists.newArrayList(new CustomDateTimeTypeContributor());
    }
});
final LocalContainerEntityManagerFactoryBean factoryBean = new 
LocalContainerEntityManagerFactoryBean();
factoryBean.setJpaProperties(jpaProperties);
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
return factoryBean;



回答4:


An alternative to what xMort did, can be registering a org.hibernate.boot.model.TypeContributor via ServiceLoader mechanism.

  1. Implement TypeContributor
package com.example.configuration;

import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.service.ServiceRegistry;

public class CustomTypeContributor implements TypeContributor {
    @Override
    public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        typeContributions.contributeType(CustomType.INSTANCE);
    }
}

  1. Create a file org.hibernate.boot.model.TypeContributor into the java module’s META-INF/services
src/main/resources/META-INF/services/org.hibernate.boot.model.TypeContributor
  1. Reference the TypeContributor, file content:
ru.eastbanctech.scs.air.transaction.repositories.StringArrayTypeContributor

As of Hibernate 5.2.17, the code that picks up the TypeContributor service can be found at org.hibernate.boot.model.process.spi.MetadataBuildingProcess#handleTypes.



来源:https://stackoverflow.com/questions/42695339/how-should-i-register-custom-hibernate-5-data-type-basictype-when-spring-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!