Skip to content

Commit

Permalink
Auto-configure Hibernate with a JsonFormatMapper.
Browse files Browse the repository at this point in the history
  • Loading branch information
nosan committed Oct 14, 2024
1 parent fadd054 commit 5aa9770
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
Expand All @@ -40,7 +41,8 @@
* @since 1.0.0
*/
@AutoConfiguration(
after = { DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class },
after = { DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class,
JacksonAutoConfiguration.class },
before = { TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
@EnableConfigurationProperties(JpaProperties.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,12 +27,14 @@

import javax.sql.DataSource;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;

import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
Expand All @@ -42,6 +44,7 @@
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration.HibernateRuntimeHints;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -51,7 +54,9 @@
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.jndi.JndiLocatorDelegate;
import org.springframework.orm.hibernate5.SpringBeanContainer;
Expand All @@ -74,6 +79,7 @@
@EnableConfigurationProperties(HibernateProperties.class)
@ConditionalOnSingleCandidate(DataSource.class)
@ImportRuntimeHints(HibernateRuntimeHints.class)
@Import(HibernateJpaConfiguration.HibernateJsonFormatMapperConfiguration.class)
class HibernateJpaConfiguration extends JpaBaseConfiguration {

private static final Log logger = LogFactory.getLog(HibernateJpaConfiguration.class);
Expand Down Expand Up @@ -226,6 +232,19 @@ private Object getNoJtaPlatformManager() {
"No available JtaPlatform candidates amongst " + Arrays.toString(NO_JTA_PLATFORM_CLASSES));
}

@ConditionalOnClass({ ObjectMapper.class, JacksonJsonFormatMapper.class })
@ConditionalOnSingleCandidate(ObjectMapper.class)
@Configuration(proxyBeanMethods = false)
static class HibernateJsonFormatMapperConfiguration {

@Bean
HibernatePropertiesCustomizer jsonFormatMapperHibernatePropertiesCustomizer(ObjectMapper objectMapper) {
return (properties) -> properties.putIfAbsent(AvailableSettings.JSON_FORMAT_MAPPER,
new JacksonJsonFormatMapper(objectMapper));
}

}

private static class NamingStrategiesHibernatePropertiesCustomizer implements HibernatePropertiesCustomizer {

private final PhysicalNamingStrategy physicalNamingStrategy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import javax.sql.DataSource;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
Expand All @@ -40,13 +41,16 @@
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.ManagedBeanSettings;
import org.hibernate.cfg.SchemaToolingSettings;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hibernate.type.format.AbstractJsonFormatMapper;
import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

Expand All @@ -59,6 +63,7 @@
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
Expand Down Expand Up @@ -367,6 +372,35 @@ void hibernatePropertiesCustomizerTakesPrecedenceOverStrategyInstancesAndNamingS
});
}

@Test
void jsonFormatMapperHibernatePropertiesCustomizerUsedAutoConfiguredObjectMapper() {
contextRunner().withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
.run(vendorProperties(
(vendorProperties) -> assertThat(vendorProperties.get(AvailableSettings.JSON_FORMAT_MAPPER))
.isInstanceOf(JacksonJsonFormatMapper.class)));
}

@Test
void jsonFormatMapperHibernatePropertiesCustomizerShouldNotOverrideJsonFormatMapperIfAlreadyConfigured() {
AbstractJsonFormatMapper jsonFormatMapper = mock(AbstractJsonFormatMapper.class);
contextRunner().withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
.withBean(HibernatePropertiesCustomizer.class,
() -> (hibernateProperties) -> hibernateProperties.put(AvailableSettings.JSON_FORMAT_MAPPER,
jsonFormatMapper))
.run(vendorProperties(
(vendorProperties) -> assertThat(vendorProperties.get(AvailableSettings.JSON_FORMAT_MAPPER))
.isSameAs(jsonFormatMapper)));
}

@Test
void jsonFormatMapperHibernatePropertiesCustomizerShouldNotBeRegisteredIfNoSingleCandidate() {
contextRunner().withBean("objectMapper1", ObjectMapper.class, ObjectMapper::new)
.withBean("objectMapper2", ObjectMapper.class, ObjectMapper::new)
.run(vendorProperties(
(vendorProperties) -> assertThat(vendorProperties.get(AvailableSettings.JSON_FORMAT_MAPPER))
.isNull()));
}

@Test
void eventListenerCanBeRegisteredAsBeans() {
contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class)
Expand Down

0 comments on commit 5aa9770

Please sign in to comment.