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
You can download the full source code of the above examples here: JPA @Embedded And @Embeddable Example