jpa

JPA @Embedded And @Embeddable Example

1. Introduction

In this post, we will examine the functionality of two JPA annotations, the JPA @Embedded and the @Embeddable. We will leverage the power of Spring Data JPA and H2 in memory database to create the code examples and run the unit test.

2. Project Setup

To run the code examples of this post, we will use the following technologies:

  • Java 8
  • Spring Data JPA 2.1.8
  • H2 1.4.199
  • Maven 3.3.3
  • Eclipse 4.10.0

Maven will be used as the tool to build the project so let’s add all the dependencies to the pom.xml:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javacodegeeks</groupId>
	<artifactId>jpa-embedded-and-embeddable</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>JPA @Embedded And @Embeddable Example</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

The Spring Data JPA is included in the Spring Boot Starter Data JPA which will auto-configure the H2 in-memory database.

3. JPA @Embedded

When we want to separate some common characteristics (fields) of an entity and put them to a separate class, then we can embed this class to the entity with the @Embedded annotation. Below is an example of the Employee class which embeds the Address class:

Employee.java

@Entity
public class Employee {

    @Id
    @GeneratedValue
    private int id;

    private String fullname;

    private int salary;

    @Embedded
    private Address address;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFullname() {
        return fullname;
    }

    public void setFullname(String fullname) {
        this.fullname = fullname;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", fullname=" + fullname + ", salary=" + salary + ", address=" + address + "]";
    }
}

In the above code, the Employee entity has 4 fields, the id, fullname, salary and address. The address is of type Address and its fields are embedded into the Employee entity by marking the address field @Embedded. In the following section we will implement the Address class.

4. JPA @Embeddable

The @Embeddable annotation should be used in conjunction with the @Embedded annotation, as it specified a class whose instances are stored as part of an owning entity. Below we create the Address class which is embedded into the Employee class.

Address.java

@Embeddable
public class Address {

    private String postcode;

    private String street;

    private String city;

    private String country;

    public String getPostcode() {
        return postcode;
    }

    public void setPostcode(String postcode) {
        this.postcode = postcode;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "Address [postcode=" + postcode + ", street=" + street + ", city=" + city + ", country=" + country + "]";
    }
}

As we see in the above class, the @Embeddable annotation is added on the class level. All the fields of the Address class are part of the Employee entity and as such of the Employee table that will be created.

5. Run the unit test

We will see how the @Embedded and @Embeddable annotations are used by running the following unit test.

JpaApplicationTest.java

@RunWith(SpringRunner.class)
@DataJpaTest
public class JpaApplicationTest {

    @Autowired
    EmployeeRepository employeeRepository;

    @Test
    public void testSaveAndGetEntity() {
        // create entities
        Address address = new Address();
        address.setPostcode("W5");
        address.setStreet("45 Broadway");
        address.setCity("London");
        address.setCountry("UK");

        Employee employee = new Employee();
        employee.setFullname("Keith Henderson");
        employee.setSalary(50000);
        employee.setAddress(address);

        // save entity
        employeeRepository.save(employee);

        // assert entity
        List employees = employeeRepository.findAll();
        assertEquals(1, employees.size());
        Employee savedEmployee = employees.get(0);
        assertTrue(employee.getId() > 0);
        assertEquals(employee.getFullname(), savedEmployee.getFullname());
        assertEquals(employee.getSalary(), savedEmployee.getSalary());
        assertEquals(employee.getAddress().getPostcode(), savedEmployee.getAddress().getPostcode());
        assertEquals(employee.getAddress().getStreet(), savedEmployee.getAddress().getStreet());
        assertEquals(employee.getAddress().getCity(), savedEmployee.getAddress().getCity());
        assertEquals(employee.getAddress().getCountry(), savedEmployee.getAddress().getCountry());
    }
}

In the code above, we create a class that is annotated with @DataJpaTest which is used to apply configuration relevant to JPA tests. The unit test saves an Employee entity along with the fields of an Address object. Then we query all the Employee entities and we validate the one we saved. Let’s run the unit test and see the output:

Output

12:47:08.476 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class com.javacodegeeks.JpaApplicationTest]
12:47:08.480 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
12:47:08.487 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
12:47:08.506 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.javacodegeeks.JpaApplicationTest] from class [org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper]
12:47:08.522 [main] INFO org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.javacodegeeks.JpaApplicationTest], using SpringBootContextLoader
12:47:08.524 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.javacodegeeks.JpaApplicationTest]: class path resource [com/javacodegeeks/JpaApplicationTest-context.xml] does not exist
12:47:08.525 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.javacodegeeks.JpaApplicationTest]: class path resource [com/javacodegeeks/JpaApplicationTestContext.groovy] does not exist
12:47:08.525 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.javacodegeeks.JpaApplicationTest]: no resource found for suffixes {-context.xml, Context.groovy}.
12:47:08.525 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.javacodegeeks.JpaApplicationTest]: JpaApplicationTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
12:47:08.591 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.javacodegeeks.JpaApplicationTest]
12:47:08.651 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [C:\Users\lkarageorgiou\Downloads\JPA @Embedded And @Embeddable Example\code\target\classes\com\javacodegeeks\JpaApplication.class]
12:47:08.652 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.javacodegeeks.JpaApplication for test class com.javacodegeeks.JpaApplicationTest
12:47:08.653 [main] DEBUG org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - @TestExecutionListeners is not present for class [com.javacodegeeks.JpaApplicationTest]: using defaults.
12:47:08.654 [main] INFO org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
12:47:08.660 [main] DEBUG org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [javax/servlet/ServletContext]
12:47:08.666 [main] INFO org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3f200884, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@4d339552, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@f0f2775, org.springframework.test.context.support.DirtiesContextTestExecutionListener@5a4aa2f2, org.springframework.test.context.transaction.TransactionalTestExecutionListener@6591f517, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@345965f2, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@429bd883, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@4d49af10, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@279ad2e3, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@58134517, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@4450d156]
12:47:08.667 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.javacodegeeks.JpaApplicationTest]
12:47:08.667 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.javacodegeeks.JpaApplicationTest]
12:47:08.668 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.javacodegeeks.JpaApplicationTest]
12:47:08.668 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.javacodegeeks.JpaApplicationTest]
12:47:08.676 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.javacodegeeks.JpaApplicationTest]
12:47:08.676 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.javacodegeeks.JpaApplicationTest]
12:47:08.678 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.javacodegeeks.JpaApplicationTest]
12:47:08.678 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.javacodegeeks.JpaApplicationTest]
12:47:08.679 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.javacodegeeks.JpaApplicationTest]
12:47:08.679 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.javacodegeeks.JpaApplicationTest]
12:47:08.683 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@55040f2f testClass = JpaApplicationTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@64c87930 testClass = JpaApplicationTest, locations = '{}', classes = '{class com.javacodegeeks.JpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@400cff1a key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@52feb982, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@17d677df, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@3d921e20, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@313f953b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].
12:47:08.683 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.javacodegeeks.JpaApplicationTest]
12:47:08.683 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.javacodegeeks.JpaApplicationTest]
12:47:08.687 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@55040f2f testClass = JpaApplicationTest, testInstance = com.javacodegeeks.JpaApplicationTest@1b083826, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@64c87930 testClass = JpaApplicationTest, locations = '{}', classes = '{class com.javacodegeeks.JpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@400cff1a key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@52feb982, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@17d677df, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@3d921e20, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@313f953b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]].
12:47:08.776 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true, server.port=-1}

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.5.RELEASE)

2019-06-03 12:47:08.991  INFO 51376 --- [           main] com.javacodegeeks.JpaApplicationTest     : Starting JpaApplicationTest on karageorgiou with PID 51376 (started by lkarageorgiou in C:\Users\lkarageorgiou\Downloads\JPA @Embedded And @Embeddable Example\code)
2019-06-03 12:47:08.993  INFO 51376 --- [           main] com.javacodegeeks.JpaApplicationTest     : No active profile set, falling back to default profiles: default
2019-06-03 12:47:09.249  INFO 51376 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2019-06-03 12:47:09.303  INFO 51376 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 47ms. Found 1 repository interfaces.
2019-06-03 12:47:09.356  INFO 51376 --- [           main] beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version
2019-06-03 12:47:09.603  INFO 51376 --- [           main] o.s.j.d.e.EmbeddedDatabaseFactory        : Starting embedded database: url='jdbc:h2:mem:e326aea1-0595-4124-8843-8b5eff6e1018;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
2019-06-03 12:47:10.125  INFO 51376 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
	name: default
	...]
2019-06-03 12:47:10.210  INFO 51376 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.3.10.Final}
2019-06-03 12:47:10.212  INFO 51376 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2019-06-03 12:47:10.400  INFO 51376 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-06-03 12:47:10.728  INFO 51376 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: drop table employee if exists
Hibernate: drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table employee (id integer not null, city varchar(255), country varchar(255), postcode varchar(255), street varchar(255), fullname varchar(255), salary integer not null, primary key (id))
2019-06-03 12:47:11.430  INFO 51376 --- [           main] o.h.t.schema.internal.SchemaCreatorImpl  : HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@3d24420b'
2019-06-03 12:47:11.434  INFO 51376 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2019-06-03 12:47:11.899  INFO 51376 --- [           main] com.javacodegeeks.JpaApplicationTest     : Started JpaApplicationTest in 3.122 seconds (JVM running for 3.756)
2019-06-03 12:47:11.920  INFO 51376 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@55040f2f testClass = JpaApplicationTest, testInstance = com.javacodegeeks.JpaApplicationTest@1b083826, testMethod = testSaveAndGetEntity@JpaApplicationTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@64c87930 testClass = JpaApplicationTest, locations = '{}', classes = '{class com.javacodegeeks.JpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@400cff1a key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@52feb982, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@17d677df, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@3d921e20, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@313f953b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@3e05586b]; rollback [true]
Hibernate: call next value for hibernate_sequence
2019-06-03 12:47:12.056  INFO 51376 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into employee (city, country, postcode, street, fullname, salary, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: select employee0_.id as id1_0_, employee0_.city as city2_0_, employee0_.country as country3_0_, employee0_.postcode as postcode4_0_, employee0_.street as street5_0_, employee0_.fullname as fullname6_0_, employee0_.salary as salary7_0_ from employee employee0_
2019-06-03 12:47:12.191  INFO 51376 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@55040f2f testClass = JpaApplicationTest, testInstance = com.javacodegeeks.JpaApplicationTest@1b083826, testMethod = testSaveAndGetEntity@JpaApplicationTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@64c87930 testClass = JpaApplicationTest, locations = '{}', classes = '{class com.javacodegeeks.JpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@400cff1a key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@52feb982, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@17d677df, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@3d921e20, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@313f953b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
2019-06-03 12:47:12.205  INFO 51376 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-06-03 12:47:12.205  INFO 51376 --- [       Thread-2] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
Hibernate: drop table employee if exists
Hibernate: drop sequence if exists hibernate_sequence

From the above output, we see in line 57 that a new employee table is created which consists of all the fields of the Employee entity and the Address class:

create table employee (
	id integer not null, 
	city varchar(255), 
	country varchar(255), 
	postcode varchar(255), 
	street varchar(255), 
	fullname varchar(255), 
	salary integer not null, 
	primary key (id)
)

We also verify from the output that there is no table creation for the Address class, its fields are only embedded into the Employee entity. Then in line 64, we insert an Employee entity into the H2 in-memory database and in line 65 we query all the records of the employee table. Finally the test asserts that the entity was saved successfully.

6. @AttributeOverrides

To override the mappings of the fields of an embedded class we can use the @AttributeOverrides annotation. Below we take the Address class and change the mapping of two of its fields.

@Embedded
@AttributeOverrides({
    @AttributeOverride(name = "postcode", column = @Column(name = "zip")),
    @AttributeOverride(name = "city", column = @Column(name = "town"))
})
private Address address;

In the above code, we override the mapping of the postcode and city fields and we name them zip and town respectively. The new field names will be applied during the creation of the employee table which will now be:

create table employee (
	id integer not null, 
	town varchar(255), 
	country varchar(255), 
	zip varchar(255), 
	street varchar(255), 
	fullname varchar(255), 
	salary integer not null, 
	primary key (id)
)

See that the postcode and city columns no longer exist and they are renamed to zip and town.

7. Conclusion

In this post, we took a look at the functionality of two JPA annotations, the @Embedded and @Embeddable using code examples with Spring Data JPA and H2 in-memory database. We also saw how the @AttributeOverrides annotation can override the mapping of the fields of an embedded class.

8. Download the Eclipse project

Download
You can download the full source code of the above examples here: JPA @Embedded And @Embeddable Example

Lefteris Karageorgiou

Lefteris is a Lead Software Engineer at ZuluTrade and has been responsible for re-architecting the backend of the main website from a monolith to event-driven microservices using Java, Spring Boot/Cloud, RabbitMQ, Redis. He has extensive work experience for over 10 years in Software Development, working mainly in the FinTech and Sports Betting industries. Prior to joining ZuluTrade, Lefteris worked as a Senior Java Developer at Inspired Gaming Group in London, building enterprise sports betting applications for William Hills and Paddy Power. He enjoys working with large-scalable, real-time and high-volume systems deployed into AWS and wants to combine his passion for technology and traveling by attending software conferences all over the world.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button