Introduction to Hibernate embeddable types

Entities vs Value types

When we create domain model we must deal with two kinds of objects: entities and value types. Entities represents objects that have some notion of identity like person or a vehicle. Person may change name or even sex but we still use the same object to represent that particular person, we only update object attributes (e.g. even if I change my name I’m still myself). Entities always have some kind of identifier that allows us to distinguish them, this may be a surrogate key like Long id or a natural key like national id/social security number in case of person.

The other kind of objects that we encounter in domain model are value types. Value types represent things like addresses, phone numbers, money amounts, currencies etc. Value types are immutable and don’t need to have any identifier - two value types are equal if they contents are equal. If we need to update value type we just create a new instance of value type with updated attributes.

What this have to do with embeddable types? In most cases embeddable types should be used to represent only value types of your domain model.
Now let’s see the code!

Embeddable in Hibernate

We will start by creating User entity that will have two embeddable value types PhoneNumber and MoneyAmount:

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

    private String username;
    private PhoneNumber contactPhoneNumber;
    private MoneyAmount availableFunds;

    // constructors, getters, setters, methods etc.
}

We don’t need to use any additional annotations to map embeddable types, we just declare them like any other field. Later we will see that we may change embeddable type mappings when needed.

Now let’s see how PhoneNumber value type looks like. Since it is our first value type I will present entire class:

@Embeddable
public class PhoneNumber implements Serializable {
    @Column(length = 16, nullable = false)
    private String phoneNumber;

    protected PhoneNumber() { }

    public PhoneNumber(String phoneNumber) {
        // You can add validation here
        this.phoneNumber = Objects.requireNonNull(phoneNumber);
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || !(o instanceof PhoneNumber)) return false;

        PhoneNumber that = (PhoneNumber) o;
        return phoneNumber.equals(that.phoneNumber);
    }

    @Override
    public int hashCode() {
        return phoneNumber.hashCode();
    }

    @Override
    public String toString() {
        return "phone: " + phoneNumber;
    }
}

First thing we should notice is that PhoneNumber instances are immutable. We can set phoneNumber using constructor, but we cannot change it. Notice also that we overriden equals() and hashCode() to provide value equality - two phone numbers are equal if they string representations are equal. To improve debugging experience we also overriden toString() method.

The other class MoneyAmount looks like this:

@Embeddable
public class MoneyAmount implements Serializable {
    private BigDecimal amount;

    @Column(length = 3, nullable = false)
    private String currency;

    // constructor getters etc.

    public MoneyAmount add(MoneyAmount m) { /* ... */ }
    public MoneyAmount subtract(MoneyAmount m) { /* ... */ }
    public boolean isLessThan(MoneyAmount other) { /* ... */ }

    // equals, hashCode, toString 
}

Notice that this class provides additional operations like add(). Because value types are immutable we cannot change already exiting MoneyAmount instance, instead we return a new object that will represent result of the add() operation.

Now let’s get to the Hibernate part. Embeddable types must be marked with @Embeddable annotation, they can contain one or more fields and every field may carry @Column mappings. Embeddable types may contain other embeddable types, but they cannot contain @Id field.

When Hibernate stores embeddable types in database they are stored in table of the entity that contains them. For example for our User entity Hibernate will generate table:

CREATE TABLE users
(
  id bigint NOT NULL, -- User

  amount numeric(19,2), -- MoneyAmount
  currency character varying(3) NOT NULL,

  phonenumber character varying(16) 
        NOT NULL, -- PhoneNumber

  username character varying(255), -- User
  
  CONSTRAINT users_pkey PRIMARY KEY (id)
)

Sometimes we want to change e.g. one of the column names of the embeddable type but only in certain containing type. For example let’s say that we want to map PhoneNumber in User to phone_number column. We may do this using @AttributeOverride annotation:

public class User {
    // ...

    @AttributeOverrides({
        @AttributeOverride(name = "phoneNumber",
                column = @Column(name = "phone_number", length = 16, nullable = false))
    })
    private PhoneNumber contactPhoneNumber;

    // ...
}

Unfortunately when we override column attributes we must redefine all of them, and syntax to do so it pretty verbose.

Now it’s time to save some ickle embeddables to database:

User bob = new User(
    "bob",
    new PhoneNumber("111-222-333"),
    new MoneyAmount(new BigDecimal(100), "EUR"));

entityManager.persist(bob);

This operation will result in SQL:

insert into users
    (amount, currency, phone_number, username, id) 
values
    (100, 'EUR', '111-222-333', 'bob', 1)

We may also update embeddable type by providing new instance:

public class User {
    // ...
    public void chargeUser(MoneyAmount amount) {
        if (availableFunds.isLessThan(amount))
            throw new IllegalStateException(
                    "User don't have enough money available.");

        availableFunds = availableFunds.subtract(amount);
    }
    // ...
}

User bob = entityManager.find(User.class, 1);

bob.chargeUser(
    new MoneyAmount(new BigDecimal(10), "EUR")
    );

This will generate UPDATE:

update users 
set
    amount=90.00,
    currency='EUR',
    phone_number='111-222-333',
    username='bob' 
where
    id=1
Embeddable types and nullability

Hibernate stores null embeddable type as NULL values in all embeddable type columns. You can save object with null embeddable type only if all embeddable type columns are nullable. When you try to do this with embeddable type that contains non-null columns Hibernate will throw exception:

Dummy d = new Dummy();
d.setPhoneNumber(null);
entityManager.persist(d);

// at commit:
// ERROR: null value in column "phonenumber" violates not-null constraint
Sharing embeddable types between entities

Embeddable types follow value semantics, this means when we save the same instance of embeddable type in two different entities and then read back these entities we will get two instances of embeddable type:

PhoneNumber phoneNumber = new PhoneNumber("111-222-333");

Dummy d1 = new Dummy();
d1.setPhoneNumber(phoneNumber);

Dummy d2 = new Dummy();
d2.setPhoneNumber(phoneNumber);

// true
System.out.println(d1.getPhoneNumber() == d2.getPhoneNumber());

entityManager.persist(d1);
entityManager.persist(d2);
entityManager.flush();
entityManager.clear();

d1 = entityManager.find(Dummy.class, d1.getId());
d2 = entityManager.find(Dummy.class, d2.getId());

// false
System.out.println(d1.getPhoneNumber() == d2.getPhoneNumber());

// true
System.out.println(d1.getPhoneNumber().equals(d2.getPhoneNumber()));

This is generally not a problem because we should compare value types using equals() anyway.

Mapping many instances of embeddable type in the same entity

Let’s say we want to extend our User entity to allow users to provide second phone number. If we only add second PhoneNumber field to the User class, Hibernate will not know how to name columns of the second PhoneNumber. This will result in the exception:

org.hibernate.MappingException: 
    Repeated column in mapping for entity: User column: phoneNumber 

We can easly fix this using @AttributeOverride annotation:

public class User {
    // ...
    private PhoneNumber contactPhoneNumber;

    @AttributeOverrides({
            @AttributeOverride(name = "phoneNumber",
                    column = @Column(name = "backup_phone_number", length = 16, nullable = false))
    })
    private PhoneNumber backupPhoneNumber;

    // ...
}

Collections of embeddable types

Embeddable types may be used as collection elements. Let’s extend our User class so that user can have any number of phone numbers:

public class User {
    // ...

    @ElementCollection
    @CollectionTable(
            name = "user_phone_numbers",
            joinColumns = @JoinColumn(name = "user_id"))
    private Set<PhoneNumber> phoneNumbers = new HashSet<>();

    public Collection<PhoneNumber> getPhoneNumbers() {
        return Collections.unmodifiableCollection(phoneNumbers);
    }

    public void addPhoneNumber(PhoneNumber number) {
        phoneNumbers.add(number);
    }

    public void removePhoneNumber(PhoneNumber number) {
        phoneNumbers.remove(number);
    }

    // ...

In this case collection elements are owned by entity class, if entity is removed all collection elements are removed as well. In database this will be represented by two tables: How embeddable collections are stored in database

Before you start using this method you must know how Hibernate will perform inserts/updates. Basically Hibernate first will remove all rows associated with given entity and then will perform many inserts (one insert per one collection element). This is horrible from performance point of view so use this mapping only with small collections.

Here is a bit of code to illustrate the point:

User b = entityManager.find(User.class, 1);
// user already have one phone number
b.addPhoneNumber(new PhoneNumber("333-555-666"));

This will result in SQL:

delete from user_phone_numbers 
where
    user_id=1

insert into user_phone_numbers
    (user_id, phoneNumber) 
values
    (1, '333-555-666')

insert into user_phone_numbers
    (user_id, phoneNumber) 
values
    (1, '111-222-333')
Allowing duplicates in collection

If we want to allow duplicates in embeddable type collection we should map it as a bag:

@ElementCollection
@CollectionTable(
        name = "user_phone_numbers",
        joinColumns = @JoinColumn(name = "user_id"))
@org.hibernate.annotations.CollectionId(
        columns = @Column(name = "phone_number_id"),
        type = @org.hibernate.annotations.Type(type = "long"),
        generator = "sequence"
)
private Collection<PhoneNumber> phoneNumbers = new ArrayList<>();

This will result in database schema: How embeddable collections are stored in database Unfortunatelly this will not improve bad performance of embeddable type collections.

You can improve performance a bit (mainly with inserts) using @OrderColumn annotation, more informations can be found in these articles:

The End

In this blog post I only scratched the surface of the embeddable types, there is more to learn about them. If you want to dig deeper I advice reading great book: Java Persistence with Hibernate 2nd.

Thats all for today, thanks for reading!

marcin-chwedczuk

A Programmer, A Geek, A Human