From 184d60ca6e8f489603c33ac38930f748522a99da Mon Sep 17 00:00:00 2001 From: Juan Moreno Date: Fri, 2 Nov 2018 10:36:21 -0300 Subject: [PATCH] Hibernate Field Naming with Spring Boot Issue: BAEL-2098 --- .../com/baeldung/naming/HibernateConfig.java | 19 ++++++ .../naming/MetadataExtractorIntegrator.java | 41 +++++++++++++ .../com/baeldung/naming/entity/Account.java | 19 ++++++ .../baeldung/naming/entity/Preference.java | 16 +++++ .../LegacyJpaImplNamingIntegrationTest.java | 58 +++++++++++++++++++ .../com/baeldung/naming/NamingConfig.java | 15 +++++ ...pringBootDefaultNamingIntegrationTest.java | 54 +++++++++++++++++ .../StrategyLegacyHbmImplIntegrationTest.java | 58 +++++++++++++++++++ 8 files changed, 280 insertions(+) create mode 100644 persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/HibernateConfig.java create mode 100644 persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/MetadataExtractorIntegrator.java create mode 100644 persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Account.java create mode 100644 persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Preference.java create mode 100644 persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/LegacyJpaImplNamingIntegrationTest.java create mode 100644 persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/NamingConfig.java create mode 100644 persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/SpringBootDefaultNamingIntegrationTest.java create mode 100644 persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/StrategyLegacyHbmImplIntegrationTest.java diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/HibernateConfig.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/HibernateConfig.java new file mode 100644 index 0000000000..897e34d406 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/HibernateConfig.java @@ -0,0 +1,19 @@ +package com.baeldung.naming; + +import org.hibernate.jpa.boot.spi.IntegratorProvider; +import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; + +import java.util.Collections; +import java.util.Map; + +/** + * Custom implementation of the {@link HibernatePropertiesCustomizer HibernatePropertiesCustomizer}. + * We can use it to set custom hibernate properties. + */ +public class HibernateConfig implements HibernatePropertiesCustomizer { + + @Override + public void customize(Map hibernateProperties) { + hibernateProperties.put("hibernate.integrator_provider", (IntegratorProvider) () -> Collections.singletonList(MetadataExtractorIntegrator.INSTANCE)); + } +} diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/MetadataExtractorIntegrator.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/MetadataExtractorIntegrator.java new file mode 100644 index 0000000000..24b5cdea64 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/MetadataExtractorIntegrator.java @@ -0,0 +1,41 @@ +package com.baeldung.naming; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; + +/** + * Custom implementation of the {@link Integrator Integrator} interface. + * We can use it to getting access to the binding metadata between entity mappings and database tables. + */ +public class MetadataExtractorIntegrator implements Integrator { + + public static final MetadataExtractorIntegrator INSTANCE = new MetadataExtractorIntegrator(); + + private Database database; + + private Metadata metadata; + + public Database getDatabase() { + return database; + } + + public Metadata getMetadata() { + return metadata; + } + + @Override + public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + + this.database = metadata.getDatabase(); + this.metadata = metadata; + + } + + @Override + public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + + } +} diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Account.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Account.java new file mode 100644 index 0000000000..6145818c5b --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Account.java @@ -0,0 +1,19 @@ +package com.baeldung.naming.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.util.List; + +@Entity +public class Account { + + @Id private Long id; + + private String defaultEmail; + + @OneToMany List preferences; + + @Column(name = "\"Secondary_Email\"") private String secondaryEmail; +} diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Preference.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Preference.java new file mode 100644 index 0000000000..928884a2c5 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/naming/entity/Preference.java @@ -0,0 +1,16 @@ +package com.baeldung.naming.entity; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Preference { + + @Id private Long id; + + private String name; + + @ManyToOne private Account account; + +} diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/LegacyJpaImplNamingIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/LegacyJpaImplNamingIntegrationTest.java new file mode 100644 index 0000000000..e68be3bed8 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/LegacyJpaImplNamingIntegrationTest.java @@ -0,0 +1,58 @@ +package com.baeldung.naming; + +import com.baeldung.naming.entity.Account; +import org.assertj.core.api.SoftAssertions; +import org.hibernate.boot.Metadata; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Table; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@DataJpaTest +@TestPropertySource(properties = { + "spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl", + "spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl" +}) +public class LegacyJpaImplNamingIntegrationTest extends NamingConfig { + + @Test + public void givenLegacyJpaImplNamingStrategy_whenCreateDatabase_thenGetStrategyNames() { + Metadata metadata = MetadataExtractorIntegrator.INSTANCE.getMetadata(); + String entity = Account.class.getCanonicalName(); + PersistentClass persistentClass = metadata.getEntityBinding(entity); + Table table = persistentClass.getTable(); + String physicalNameExpected = "Secondary_Email"; + String implicitNameExpected = "defaultEmail"; + String tableNameExpected = "Account"; + + String tableNameCreated = table.getName(); + boolean columnNameIsQuoted = table + .getColumn(3) + .isQuoted(); + String physicalNameCreated = table + .getColumn(3) + .getName(); + String implicitNameCreated = table + .getColumn(2) + .getName(); + + SoftAssertions.assertSoftly(softly -> { + softly + .assertThat(columnNameIsQuoted) + .isTrue(); + softly + .assertThat(tableNameCreated) + .isEqualTo(tableNameExpected); + softly + .assertThat(physicalNameCreated) + .isEqualTo(physicalNameExpected); + softly + .assertThat(implicitNameCreated) + .isEqualTo(implicitNameExpected); + }); + } +} diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/NamingConfig.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/NamingConfig.java new file mode 100644 index 0000000000..c3ef37aeb4 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/NamingConfig.java @@ -0,0 +1,15 @@ +package com.baeldung.naming; + +import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +public class NamingConfig { + @TestConfiguration + static class Config { + @Bean + public HibernatePropertiesCustomizer customizer() { + return new HibernateConfig(); + } + } +} diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/SpringBootDefaultNamingIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/SpringBootDefaultNamingIntegrationTest.java new file mode 100644 index 0000000000..089430aabb --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/SpringBootDefaultNamingIntegrationTest.java @@ -0,0 +1,54 @@ +package com.baeldung.naming; + +import com.baeldung.naming.entity.Account; +import org.assertj.core.api.SoftAssertions; +import org.hibernate.boot.Metadata; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Table; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@DataJpaTest +@TestPropertySource(properties = { + "spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy", + "spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy" +}) +public class SpringBootDefaultNamingIntegrationTest extends NamingConfig { + + @Test + public void givenDefaultBootNamingStrategy_whenCreateDatabase_thenGetStrategyNames() { + Metadata metadata = MetadataExtractorIntegrator.INSTANCE.getMetadata(); + String entity = Account.class.getCanonicalName(); + PersistentClass persistentClass = metadata.getEntityBinding(entity); + Table table = persistentClass.getTable(); + String physicalNameExpected = "secondary_email"; + String implicitNameExpected = "default_email"; + String tableNameExpected = "account"; + + String tableNameCreated = table.getName(); + String physicalNameCreated = table + .getColumn(3) + .getName(); + String implicitNameCreated = table + .getColumn(2) + .getName(); + + SoftAssertions softly = new SoftAssertions(); + softly + .assertThat(tableNameCreated) + .isEqualTo(tableNameExpected); + softly + .assertThat(physicalNameCreated) + .isEqualTo(physicalNameExpected); + softly + .assertThat(implicitNameCreated) + .isEqualTo(implicitNameExpected); + softly.assertAll(); + } +} diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/StrategyLegacyHbmImplIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/StrategyLegacyHbmImplIntegrationTest.java new file mode 100644 index 0000000000..046755d9bc --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/naming/StrategyLegacyHbmImplIntegrationTest.java @@ -0,0 +1,58 @@ +package com.baeldung.naming; + +import com.baeldung.naming.entity.Preference; +import org.assertj.core.api.SoftAssertions; +import org.hibernate.boot.Metadata; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Table; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Collection; + +@RunWith(SpringRunner.class) +@DataJpaTest +@TestPropertySource(properties = { + "spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl", + "spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl", +}) +public class StrategyLegacyHbmImplIntegrationTest extends NamingConfig { + + @Test + public void givenLegacyHbmImplNamingNamingStrategy_whenCreateDatabase_thenGetStrategyNames() { + Metadata metadata = MetadataExtractorIntegrator.INSTANCE.getMetadata(); + String entity = Preference.class.getCanonicalName(); + PersistentClass persistentClass = metadata.getEntityBinding(entity); + Collection tables = metadata + .getDatabase() + .getDefaultNamespace() + .getTables(); + Table preferenceTable = persistentClass.getTable(); + String tableNameExpected = "Account_preferences"; + Table accountPreferencesTable = tables + .stream() + .filter(table -> table + .getName() + .equals(tableNameExpected)) + .findFirst() + .get(); + String implicitNameExpected = "account"; + + String implicitNameCreated = preferenceTable + .getColumn(3) + .getName(); + String tableNameCreated = accountPreferencesTable.getName(); + + SoftAssertions.assertSoftly(softly -> { + softly + .assertThat(implicitNameCreated) + .isEqualTo(implicitNameExpected); + softly + .assertThat(tableNameCreated) + .isEqualTo(tableNameExpected); + }); + } +}