JPA / Hibernate @Embeddable and @Embedded Example with Spring Boot

In this article, you’ll learn how to use Embeddable types in hibernate to map a linear database table into an Entity class composed of various value types.

Consider the following table which stores information about the users of an application. The information includes user’s basic details like name, email and his address -

JPA, Hibernate Embeddable Types Table Example

From the database perspective, the table structure is fine. But If we need to map this table into an entity class in our application, we might want to abstract out the Name and Address details into separate classes for better abstraction and re-usability -

Hibernate Why Embeddable Types

Embeddable types can be re-used across your project by any number of entities. We’ll create a project from scratch and learn how to map the above users table into an entity class that abstracts out the name and address details into separate classes.

Creating the Application

We’ll use Spring Boot CLI to generate our application. Open your terminal and type the following command to generate the application -

spring init -n=jpa-embeddable-demo -d=web,jpa,mysql --package-name=com.example.jpa jpa-embeddable-demo

You can also use Spring Initializr web app to generate the application. Follow the instructions below to generate the app using Spring Initializr web tool -

  1. Head over to http://start.spring.io
  2. Enter Artifact as “jpa-embeddable-demo”
  3. Click Options dropdown to see all the options related to project metadata.
  4. Change Package Name to “com.example.jpa”
  5. Select Web, JPA and Mysql dependencies.
  6. Click Generate to generate and download the project.

Following is the directory structure of the complete application for your reference -

JPA, Hibernate, Spring Boot Embeddable Type Example Directory Structure"

Your bootstrapped project won’t have model and repository packages and all the other classes at this point. We’ll create them as we proceed to the next sections.

Configuring the Database and Hibernate Log levels

We’ll need to configure the MySQL database URL, username, and password. Open src/main/resources/application.properties file and add the following properties to it -

# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_embeddable_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root

# Hibernate

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE

I have specified the Hibernate’s ddl-auto property to update. This will automatically create/update the database tables whenever we update the corresponding entity class in our application.

Note that, You’ll need to create a database named jpa_embeddable_demo. Also, don’t forget to change the spring.datasource.username and spring.datasource.password properties as per your MySQL installation.

Defining the Domain Model

Let’s first define the embeddable types that will be embedded in the User model. We’ll create a package named model inside com.example.jpa package and add all the model classes in this package.

Embeddable Types

We use JPA’s @Embeddable annotation to declare that a class is meant to be embedded by other entities.

1. Name

package com.example.jpa.model;

import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Embeddable
public class Name {
    @NotNull
    @Size(max = 40)
    private String firstName;

    @Size(max = 40)
    private String middleName;

    @Size(max = 40)
    private String lastName;

    public Name() {

    }

    public Name(String firstName, String middleName, String lastName) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
    }

    // Getters and Setters (Omitted for brevity)
}

2. Address

package com.example.jpa.model;

import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Embeddable
public class Address {
    @NotNull
    @Size(max = 100)
    private String addressLine1;

    @NotNull
    @Size(max = 100)
    private String addressLine2;

    @NotNull
    @Size(max = 100)
    private String city;

    @NotNull
    @Size(max = 100)
    private String state;

    @NotNull
    @Size(max = 100)
    private String country;

    @NotNull
    @Size(max = 6)
    private String zipCode;


    public Address() {

    }

    public Address(String addressLine1, String addressLine2, String city, 
                   String state, String country, String zipCode) {
        this.addressLine1 = addressLine1;
        this.addressLine2 = addressLine2;
        this.city = city;
        this.state = state;
        this.country = country;
        this.zipCode = zipCode;
    }

    // Getters and Setters (Omitted for brevity)
}

User Entity

Let’s now create the User model that will embed the Name and Address types -

package com.example.jpa.model;

import org.hibernate.validator.constraints.Email;
import javax.persistence.*;
import javax.validation.constraints.NotNull;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private Name name;

    @NotNull
    @Email
    @Column(unique = true)
    private String email;

    @Embedded
    @AttributeOverrides(value = {
        @AttributeOverride(name = "addressLine1", column = @Column(name = "house_number")),
        @AttributeOverride(name = "addressLine2", column = @Column(name = "street"))
    })
    private Address address;

    public User() {

    }

    public User(Name name, String email, Address address) {
        this.name = name;
        this.email = email;
        this.address = address;
    }

    // Getters and Setters (Omitted for brevity)
}

We use JPA’s @Embedded annotation to embed a type in the model class. Notice the use of @AttributeOverrides and @AttributeOverride annotations. These annotations help you customize/override the fields of embeddable types.

Creating the Repository

Next, Let’s create the repository for accessing the user’s data from the database. First, create a package named repository in the com.example.jpa package, then add the following interface inside the repository package -

package com.example.jpa.repository;

import com.example.jpa.model.Name;
import com.example.jpa.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}

We’ve extended the UserRepository from Spring Data JPA’s JpaRepository interface. The JpaRepository interface contains methods for all the CRUD operations on the entity. Spring Boot automatically injects an implementation of this interface called SimpleJpaRepository at runtime.

This helps us perform all the CRUD operations on the entity without implementing anything ourselves.

Testing the Embeddable and Embedded Setup

Finally, Let’s write some code to test our Embeddable type setup. Open the main class JpaEmbeddableDemoApplication.java and replace it with the following code -

package com.example.jpa;

import com.example.jpa.model.Address;
import com.example.jpa.model.Name;
import com.example.jpa.model.User;
import com.example.jpa.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JpaEmbeddableDemoApplication implements CommandLineRunner {

    @Autowired
    private UserRepository userRepository;

    public static void main(String[] args) {
        SpringApplication.run(JpaEmbeddableDemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // Cleanup the users table
        userRepository.deleteAllInBatch();

        // Insert a new user in the database
        Name name = new Name("Rajeev", "Kumar", "Singh");
        Address address = new Address("747", "Golf View Road", "Bangalore", "Karnataka", "India", "560008");
        User user = new User(name, "rajeev@callicoder.com", address);

        userRepository.save(user);
    }
}

In the above class, I’ve implemented Spring’s CommandLineRunner interface and written the code to test our setup in the run() method. The run() method is called when the application is successfully started.

In the run() method, we first clean up the user’s table and then insert a new User in the database.

Running the Application

You can run the application by typing the following command from the root directory of the project -

mvn spring-boot:run

Check out the logs to see the SQL statements that hibernate has executed -

org.hibernate.SQL : delete from users
org.hibernate.SQL : insert into users (city, country, house_number, state, street, zip_code, email, first_name, last_name, middle_name) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

If you check the users table in MySQL after running the application, you’ll find the entry that we inserted using hibernate.

mysql> select * from users;
+----+------------+-------------+-----------+-----------------------+--------------+----------------+-----------+-----------+---------+----------+
| id | first_name | middle_name | last_name | email                 | house_number | street         | city      | state     | country | zip_code |
+----+------------+-------------+-----------+-----------------------+--------------+----------------+-----------+-----------+---------+----------+
|  1 | Rajeev     | Kumar       | Singh     | rajeev@callicoder.com | 747          | Golf View Road | Bangalore | Karnataka | India   | 560008   |
+----+------------+-------------+-----------+-----------------------+--------------+----------------+-----------+-----------+---------+----------+
1 row in set (0.00 sec)

Notice how the table structure is linear but at the application level, we abstracted out the Name and Address details into separate classes.

Spring Data JPA Query Methods using Embeddable Types

Spring Data JPA has an excellent feature that generates queries from method names that we specify in the repository.

For example, To find a User with an email, you can add a query method like the following in the UserRepository and Spring Data JPA will automatically generate a query from it without you having to implement anything -

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Find user by email
    User findByEmail(String email);
}

You can check out the official Spring Data JPA’s documentation for understanding more about Query methods.

So the idea is that you write a query method using the field names of the entity class. When you use an embeddable type and you need to query the Entity using a field of the embeddable type, then you can specify query methods like this -

// Find users by firstName
List<User> findByNameFirstName(String firstName);

// Find users by lastName
List<User> findByNameLastName(String lastName);

// Find users by country
List<User> findByAddressCountry(String country);

Conclusion

That’s all in this article guys. I hope you understood the idea of Embeddable types in JPA and Hibernate.

You can find the entire code for the application that we built in this article on my jpa-hibernate-tutorials github repository.

Feel free to fork the project and build upon it. Thanks for reading. See you in the next post.