Skip to content

Commit 0b2f918

Browse files
committed
Auto-configure Hibernate with a JsonFormatMapper
1 parent 7a6a7d1 commit 0b2f918

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import org.springframework.boot.autoconfigure.AutoConfiguration;
2323
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25+
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
2526
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
2627
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
2728
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
@@ -40,7 +41,8 @@
4041
* @since 1.0.0
4142
*/
4243
@AutoConfiguration(
43-
after = { DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class },
44+
after = { DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class,
45+
JacksonAutoConfiguration.class },
4446
before = { TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
4547
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
4648
@EnableConfigurationProperties(JpaProperties.class)

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,12 +27,14 @@
2727

2828
import javax.sql.DataSource;
2929

30+
import com.fasterxml.jackson.databind.ObjectMapper;
3031
import org.apache.commons.logging.Log;
3132
import org.apache.commons.logging.LogFactory;
3233
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
3334
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
3435
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
3536
import org.hibernate.cfg.AvailableSettings;
37+
import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
3638

3739
import org.springframework.aot.hint.MemberCategory;
3840
import org.springframework.aot.hint.RuntimeHints;
@@ -42,6 +44,7 @@
4244
import org.springframework.aot.hint.TypeReference;
4345
import org.springframework.beans.factory.ObjectProvider;
4446
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
47+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4548
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
4649
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration.HibernateRuntimeHints;
4750
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -51,7 +54,9 @@
5154
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
5255
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
5356
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
57+
import org.springframework.context.annotation.Bean;
5458
import org.springframework.context.annotation.Configuration;
59+
import org.springframework.context.annotation.Import;
5560
import org.springframework.context.annotation.ImportRuntimeHints;
5661
import org.springframework.jndi.JndiLocatorDelegate;
5762
import org.springframework.orm.hibernate5.SpringBeanContainer;
@@ -74,6 +79,7 @@
7479
@EnableConfigurationProperties(HibernateProperties.class)
7580
@ConditionalOnSingleCandidate(DataSource.class)
7681
@ImportRuntimeHints(HibernateRuntimeHints.class)
82+
@Import(HibernateJpaConfiguration.HibernateJsonFormatMapperConfiguration.class)
7783
class HibernateJpaConfiguration extends JpaBaseConfiguration {
7884

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

235+
@ConditionalOnClass({ ObjectMapper.class, JacksonJsonFormatMapper.class })
236+
@ConditionalOnSingleCandidate(ObjectMapper.class)
237+
@Configuration(proxyBeanMethods = false)
238+
static class HibernateJsonFormatMapperConfiguration {
239+
240+
@Bean
241+
HibernatePropertiesCustomizer jsonFormatMapperHibernatePropertiesCustomizer(ObjectMapper objectMapper) {
242+
return (properties) -> properties.putIfAbsent(AvailableSettings.JSON_FORMAT_MAPPER,
243+
new JacksonJsonFormatMapper(objectMapper));
244+
}
245+
246+
}
247+
229248
private static class NamingStrategiesHibernatePropertiesCustomizer implements HibernatePropertiesCustomizer {
230249

231250
private final PhysicalNamingStrategy physicalNamingStrategy;

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import javax.sql.DataSource;
3232

33+
import com.fasterxml.jackson.databind.ObjectMapper;
3334
import com.zaxxer.hikari.HikariDataSource;
3435
import jakarta.persistence.EntityManager;
3536
import jakarta.persistence.EntityManagerFactory;
@@ -40,13 +41,16 @@
4041
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
4142
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
4243
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
44+
import org.hibernate.cfg.AvailableSettings;
4345
import org.hibernate.cfg.ManagedBeanSettings;
4446
import org.hibernate.cfg.SchemaToolingSettings;
4547
import org.hibernate.dialect.H2Dialect;
4648
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
4749
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
4850
import org.hibernate.internal.SessionFactoryImpl;
4951
import org.hibernate.jpa.HibernatePersistenceProvider;
52+
import org.hibernate.type.format.AbstractJsonFormatMapper;
53+
import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
5054
import org.junit.jupiter.api.Disabled;
5155
import org.junit.jupiter.api.Test;
5256

@@ -59,6 +63,7 @@
5963
import org.springframework.boot.autoconfigure.AutoConfigurations;
6064
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
6165
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
66+
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
6267
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
6368
import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration;
6469
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
@@ -367,6 +372,35 @@ void hibernatePropertiesCustomizerTakesPrecedenceOverStrategyInstancesAndNamingS
367372
});
368373
}
369374

375+
@Test
376+
void jsonFormatMapperHibernatePropertiesCustomizerUsedAutoConfiguredObjectMapper() {
377+
contextRunner().withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
378+
.run(vendorProperties(
379+
(vendorProperties) -> assertThat(vendorProperties.get(AvailableSettings.JSON_FORMAT_MAPPER))
380+
.isInstanceOf(JacksonJsonFormatMapper.class)));
381+
}
382+
383+
@Test
384+
void jsonFormatMapperHibernatePropertiesCustomizerShouldNotOverrideJsonFormatMapperIfAlreadyConfigured() {
385+
AbstractJsonFormatMapper jsonFormatMapper = mock(AbstractJsonFormatMapper.class);
386+
contextRunner().withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
387+
.withBean(HibernatePropertiesCustomizer.class,
388+
() -> (hibernateProperties) -> hibernateProperties.put(AvailableSettings.JSON_FORMAT_MAPPER,
389+
jsonFormatMapper))
390+
.run(vendorProperties(
391+
(vendorProperties) -> assertThat(vendorProperties.get(AvailableSettings.JSON_FORMAT_MAPPER))
392+
.isSameAs(jsonFormatMapper)));
393+
}
394+
395+
@Test
396+
void jsonFormatMapperHibernatePropertiesCustomizerShouldNotBeRegisteredIfNoSingleCandidate() {
397+
contextRunner().withBean("objectMapper1", ObjectMapper.class, ObjectMapper::new)
398+
.withBean("objectMapper2", ObjectMapper.class, ObjectMapper::new)
399+
.run(vendorProperties(
400+
(vendorProperties) -> assertThat(vendorProperties.get(AvailableSettings.JSON_FORMAT_MAPPER))
401+
.isNull()));
402+
}
403+
370404
@Test
371405
void eventListenerCanBeRegisteredAsBeans() {
372406
contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class)

0 commit comments

Comments
 (0)