Tracking the identity of ENTITIES is essential, but attaching identity to other objects can hurt system performance, add analytical work, and muddle the model by making all objects look the same.
~ Eric Evans 2003
In my previous post I looked at the Domain Driven Design concept of Value Objects as outlined by Eric Evans (2003). In this second post I want to show how we can persist such objects to a relational database using JPA.
In this example I’m using H2 as the database and Hibernate as the JPA provider. It has also been tested with EclipseLink. Source code is available on GitHub.
Mapping a Value Object in JPA using @Embeddable
The JPA Specification recognises that not everything is an Entity:
An entity may use other fine-grained classes to represent entity state. Instances of these classes, unlike entity instances, do not have persistent identity of their own. Instead, they exist only as part of the state of the entity to which they belong
~ JSR 338 (Java Persistence API 2.1)
These Embeddable classes provides a convenient mapping for Value Objects. Here are the pertinent parts of the Month class from part 1, annotated as a JPA Embeddable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Embeddable
public class Month implements Comparable<Month> {
@Column(name="MONTH")
private final String value;
@SuppressWarnings("unused")
private Month() {
this.value = null;
}
public Month(String value) {
if(!isValid(value)) {
throw new DomainException("Not a valid month " + value);
}
this.value = value;
}
// ... code omitted for brevity ...
}
In line with the JPA Specification, we provide a no-argument constructor. Consequently we must also provide a value for any final fields within this constructor. To prevent instantiation of this class without passing in a value we make this constructor private, and add a @SuppressWarnings annotation to remove the ensuing ‘never used locally’ warning.
This is an example of JPA making demands upon how we construct our domain classes. It is in direct opposition to DDD which encourages us to only include what is required for the domain. To use DDD Value Objects in JPA some compramises may have to be made.
To see Month in action we include it in the Entity class Foo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Table(name="FOO")
public class Foo {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID")
private Long entityId;
@Version
@Column(name="VERSION")
private Integer version;
@Column(name="BAA")
private String baa;
@Embedded
private Month birthMonth;
// ... code omitted for brevity ...
}
When we launch the application Hibernate issues the following SQL to create the table:
1
2
3
4
5
6
7
create table FOO (
ID bigint generated by default as identity,
BAA varchar(255),
MONTH varchar(6),
VERSION integer,
primary key (ID)
)
Overiding JPA Attributes in Value Objects
If we wanted to use the same Value Object in two places in the same Entity, this would give us two columns with the same name, which would be invalid. Neither would this work if we wanted a different column name depending on the Entity the Value Object is embedded into.
JPA provides the @AttributeOverride annotation for this purpose. As the name suggests, it is used to override the mapping of an Entity field, to allow for a different configuration in the database table:
1
2
3
4
5
6
7
@Embedded
@AttributeOverride(name = "value", column = @Column(name = "BIRTH_MONTH"))
private Month birthMonth;
@Embedded
@AttributeOverride(name = "value", column = @Column(name = "FOO_MONTH"))
private Month fooMonth;
Here is the new SQL produced by Hibernate:
1
2
3
4
5
6
7
8
create table FOO (
ID bigint generated by default as identity,
BAA varchar(255),
BIRTH_MONTH varchar(255),
FOO_MONTH varchar(255),
VERSION integer,
primary key (ID)
)
The presence of AttributeOverride means an Entity can specify its own configuration for any Embeddable classes it uses. Which makes Embeddables re-usable across any number of Entities.
Overiding JPA Attributes in Composite Value Objects
In part 1 of this post I gave the example of MonthRange as a composite Value Object. Here is what it looks like as an Embeddable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Embeddable
public class MonthRange {
@AttributeOverride(name = "value", column = @Column(name = "START_MONTH"))
private final Month start;
@AttributeOverride(name = "value", column = @Column(name = "END_MONTH"))
private final Month end;
@SuppressWarnings("unused")
private MonthRange() {
this.start = null;
this.end = null;
}
public MonthRange(Month start, Month end) {
if(!isValid(start, end)) {
throw new DomainException("Not a valid month range");
}
this.start = start;
this.end = end;
}
// ... code omitted for brevity ...
}
MonthRange is an Embeddable object, which is itself composed of Embeddable objects. If we had two MonthRange fields in an Entity, that would translate to four different Month columns in the database.
To override mappings at multiple levels of embedding dot notation is used in the name element to specify fields within fields.
Here is how we override the nested column configuration for these four Months from within out Foo Entity class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
@Table(name="FOO")
public class Foo {
// ... code omitted for brevity ...
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "start.value", column = @Column(name = "CURRENT_START_MONTH")),
@AttributeOverride(name = "end.value", column = @Column(name = "CURRENT_END_MONTH"))
})
private MonthRange currentMonthRange;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "start.value", column = @Column(name = "PREVIOUS_START_MONTH")),
@AttributeOverride(name = "end.value", column = @Column(name = "PREVIOUS_END_MONTH"))
})
private MonthRange previousMonthRange;
// ... code omitted for brevity ...
}
Here’s what it produces on Hibernate:
1
2
3
4
5
6
7
8
9
10
11
12
create table FOO (
ID bigint generated by default as identity,
BAA varchar(255),
BIRTH_MONTH varchar(255),
CURRENT_END_MONTH varchar(255),
CURRENT_START_MONTH varchar(255),
FOO_MONTH varchar(255),
PREVIOUS_END_MONTH varchar(255),
PREVIOUS_START_MONTH varchar(255),
VERSION integer,
primary key (ID)
)
Collections of Embeddables
A persistent field or property of an entity or embeddable class may correspond to a collection of a basic type or embeddable class
~ JSR 338 (Java Persistence API 2.1)
JPA allows for collections of Embeddables via its ElementCollection mapping. In this scenario the Embeddables must be held in a separate table rather than being embedded in the Entities table. Similar to a OneToMany association, but without any requirement for inverse mapping. This is how we would add a collection of Months to our Foo Entity:
1
2
3
@ElementCollection
@CollectionTable(name="FOO_MONTH", joinColumns=@JoinColumn(name="FOO_ID"))
private Collection<Month> months = new ArrayList<Month>();
Here’s what Hibernate does with it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
create table FOO (
ID bigint generated by default as identity,
BAA varchar(255),
BIRTH_MONTH varchar(255),
CURRENT_END_MONTH varchar(255),
CURRENT_START_MONTH varchar(255),
FOO_MONTH varchar(255),
PREVIOUS_END_MONTH varchar(255),
PREVIOUS_START_MONTH varchar(255),
VERSION integer,
primary key (ID)
)
create table FOOBAA_MONTH (
FOO_ID bigint not null,
MONTH varchar(255)
)
alter table FOOBAA_MONTH
add constraint FK_f3gxv3hla3nch460sx3ss6oex
foreign key (FOO_ID)
references FOO
An important restriction on collections of Embeddable classes is that:
An embeddable class (including an embeddable class within another embeddable class) that is contained within an element collection must not contain an element collection
~ JSR 338 (Java Persistence API 2.1)
Which means we cannot nest a collection of Embeddables within another collection of Embeddables. While this restriction may seem like an ‘edge case’, it can sometimes force us to use an Entity in place of a Value Object.
Conclusion
In Part 1 of this post I looked at the Domain Driven Design concept of Value Objects and outlined a way of creating such an object by following Eric Evans’ advice. In this follow-up post I have tried to show how Embeddables provide an obvious means of mapping Value Objects to a relational database when using JPA.
I’ve also tried to show how producing these mappings is not always such a ‘clean’ process. Compromises have to be made between the tenents of DDD and the platform used for its implementation.
Source code for the examples shown here are available on GitHub.