Mapping Concepts

We start with aggregation and composition relationships

Entities and values types have a relationship where the entity has a primary key the value types are references inside the entity, as you can see in the below diaram the User is an entity and the Adress is a value type which belows to an entity. Value types are identified through owning entity. This means that if the entity is deleted then the value types of that entity are also deleted.


Component mapping is where more classes are used than tables, for example we have a Person and Address classes but only one person table that contains all the data, the component is part of the whole and if the whole is destroyed then the component is also destroyed and thus this has a composition relationship and is a value type, we use the @Embeddable annotation

Person class
@Embedded
private Address address;
Address class
@Embeddable  			// component has no individual identity (no id)
public class Address {
	
  private String street;
  private String city;
  private String zipcode;
	
  public Address() {}
  public Address(String street, String city, String zipcode) {
	this.street = street;
	this.city = city;
	this.zipcode = zipcode;
  }	
}

Cascades allow us to perform one save on a entity that many have many references to other objects for example if a ClassRoom object has a reference to a Student you dont want to be saving both the ClassRoom and Student separately, you would like Hibernate to get all the objects associated with the entity and to persist all objects (all objects within the object graph) in one persist statement, to do this we can use cascade, this can have a number of types, I have listed the commonly used ones below:

cascade
// either persist or remove the guide entity if this Student entity is persisted or deleted
@Entity
public class Student {
	
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;		

  @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})   
  @JoinColumn(name="guide_id")
  private Guide guide;
	
  ......
}

As a note its not best practice to use business related data as primary keys, for example National Insurance number, Passort number, its best to a non-business related id, you should use a synthetic identifier with no business meaning.

Good id candidates
@Id
@GenerateValue
private Long id;

// Below are not good candidates for id's
private String isbn;
private String passportNumber;

One-To-Many and Many-To-One

Many-To-one means that one Entity (row in a table) is mapped to multiple entities (rows in another table), the @ManyToOneannotation allows you to map a Foreign Key column which uses the @JoinColumn annotation for the column name, One-to-many mapping (@OneToMany annotation) allows us to map bi-directional relationships. One-to-many vs Many-to-one is a matter of perspective. Unidirectional vs Bidirectional will not affect the mapping but will make difference on how you can access your data.


When you setup a relationship one side will become the owner and the other the inverse end

Owner of the Relationship
Student Class
@ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE}) 	// many students can have one guide
@JoinColumn(name="guide_id")       				// the student table will have a guide_id column that maps to the guide table
private Guide guide;
Inverse End (has mappedBy - Bi-Directional)
Guide Class
(Uni-Directional)
// No mention of students and hence ony uni-directional		
@Entity
public class Guide {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;

	private String name;
	
	public Guide() {}
	public Guide(String name) {
	  this.name = name;
	}
}
Guide Class
(Bi-Directional)
// WE add code to make it bi-directional
@Entity
public class Guide {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	private String name;
	
	public Guide() {}
	public Guide(String name) {
	  this.staffId = staffId;
	  this.name = name;
	  this.salary = salary;
	}
	
	// These are the parts to make it bi-directional 
	// mappedBy means its not the owner of the relationship
	
	@OneToMany(mappedBy="guide", cascade={CascadeType.PERSIST}) 		// one guide can have many students
	private Set<Student> students = new HashSet<Student>();	
	
	public Set<Student> getStudents() {
	  return students;
	}
	
	// used to update the student as guide is not the owner
	public void addStudent(Student student) {
	  students.add(student);
	  student.setGuide(this);
	}
}

One-To-One

One-To-One relationship means that its only possible to have a one to one relationship for example a person can only have one passport number, NHS number or National Insurance Number, etc. You use the @OneToOne annotation and the @JoinColumn and the relationship is bi-directional. The main difference between a OneToOne and a ManyToOne relationship in JPA is that a ManyToOne always contains a foreign key from the source object's table to the target object's table, whereas a OneToOne relationship the foreign key may either be in the source object's table or the target object's table


We also use the unique attribute which means that no other entity can have the same passport id number.

Owner of the Relationship
Person class
@Entity
public class Person {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	private String name;
	
	@OneToOne(cascade={CascadeType.PERSIST}) 		// will have a join column called passport_id
	@JoinColumn(name="passport_id", unique=true) 		// use unique to make sure no one else can have same passport id		
	private Passport passport;
	
	// constructor, getters and setters, etc
}
Inverse End (has mappedBy)
Passport class
@Entity
public class Passport {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	@Column(name="passport_number")
	private String passportNumber;
	
	@OneToOne(mappedBy="passport")
	private Customer customer;

	// constructor, getters and setters, etc
}

Many-To-Many

In a Many-to-Many relationship, both sides can relate to multiple instances of the other side, this also means that it has a bi-directional relationship, in the diagram below a bank can have many customers and the customer could be associated with many banks, we use the @ManyToMany annotation.


A join table is used to map the relationships, we use the @JoinTable annotation to do this for us, also remember that the inverse end is not responsible for the relationship and thus will not update the join table, in summary the inverse end will be ignored when updating the relationship values in the join table, only the owner can update the join table.


Owner of the Relationship
Movie class
@Entity
public class Movie {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	private String name;
	
	@ManyToMany(cascade={CascadeType.PERSIST})
        @JoinTable( 						// a join table is used to map the relationship
            name="movie_actor",
            joinColumns={@JoinColumn(name="movie_id")},
            inverseJoinColumns={@JoinColumn(name="actor_id")}
    	)	
	private Set<Actor> actors = new HashSet<Actor>();	
	
	// Constructors, Getters and Setters
}
Inverse End (has mappedBy)
Actor class
@Entity
public class Actor {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	private String name;
	
	@ManyToMany(mappedBy="actors")
	private Set<Movie> movies = new HashSet<Movie>();
	
	// Constructors, Getters and Setters
}

Mapping Enums

I have already covered Enumurations (enums) in my Java Reference section, Hibernate also allows you to mapp enums to a table column. You use the @Enumerated annotation and specify the type

Using eumerations is not that much different that using them in plain Java as per the diagram below


Enums example
public enum EmployeeStatus {
	FULL_TIME, 
	PART_TIME, 
	CONTRACT
}
Employee class
@Entity
public class Employee {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	private String name;
	
	@Column(name="employee_id", unique=true)
	private String employeeId;
	
	@Enumerated 								// default is to use ordinal
	//@Enumerated(EnumType.STRING) 						// use text
	//@Enumerated(EnumType.ORDINAL) 					// use the numbering
	@Column(name="employee_status")
	private EmployeeStatus employeeStatus;

	// Constructors, Getters and Setters
	
}

Mapping Collections

Hibernate can persist a Collection of basic data types (Strings, etc), for example as shown in the below diagram you have a Collection of String data type called nickNames, Hibernate creates a separate table that the entity points to, you use the @ElementCollection and @CollectionTable annotations or you can use the @AttributeOverrides if using a @Embeddable object

Below are two ways you use Collections of data types

Address class
@Embeddable
public class Address {
	
  private String street;
  private String city;
  private String zipcode;
	
  public Address() {}
  public Address(String street, String city, String zipcode) {
    this.street = street;
    this.city = city;
    this.zipcode = zipcode;
  }	
}
Using Collections
@Entity
public class Friend{

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	private String name;
	private String email;

	@ElementCollection
	@CollectionTable(name = "friend_nickname", joinColumns=@JoinColumn(name = "friend_id"))
	@Column(name = "nickname")
	private Collection<String> nicknames = new ArrayList<String>();

	//collection of embeddable Address object [with default address specfic column names being overriden using @AttributeOverride]
	/*
	@AttributeOverrides( {
		@AttributeOverride(name="street", column=@Column(name="address_street")),
		@AttributeOverride(name="city", column=@Column(name="address_city")),
		@AttributeOverride(name="zipcode", column=@Column(name="address_zipcode"))
	} )
	private Collection<Address> addresses = new ArrayList<Address>();
	*/
	
	/*public Collection<String> getNicknames() {
		return nicknames;
	}*/
	public Collection<Address> getAddresses() {
		return addresses;
	}
	
	// Constructors, Getters, Setters, etc
}

Composite Keys

Composite Keys are a combination of more than 1 table column that identifies the uniqueness of a record, normally we use a single primary key contained in a single column but instead we could use multiple columns as a primary key, in the below diagram we use the company_id and the employee_id to unqiuely identify a row in the table, so no two rows can have the same company_id and the employee_id's, In the entity we use the @EmbeddedId anotation. You also create equals and hashCode methods this allows the comparing of two objects and thus the uniqueness of a row.


In the example below the parent will have a primary key of both firstname and lastname, also the child class will use the @JoinColumns annotation to refrence the parent composite key. For forgien-key association you can use the below


Parent class
@Entity
public class Parent {
	
  @EmbeddedId
  private ParentPrimaryKey parentPrimaryKey;
	
  @OneToMany(mappedBy="parent", cascade={CascadeType.PERSIST})
  private Set<Child>children = new HashSet<Child>();

  public Parent() { }
  public Parent(ParentPrimaryKey parentPrimaryKey) {
    this.parentPrimaryKey = parentPrimaryKey;
  }	
	
  public void addChild(Child child) {
    child.setParent(this);
    this.children.add(child);
 }
}
ParentPrimaryKey
@Embeddable
public class ParentPrimaryKey implements Serializable {
	
  private String firstname;
  private String lastname;
	
  public ParentPrimaryKey() {}	
  public ParentPrimaryKey(String firstname, String lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }	
	
  // toString, hashCode and equals	
}
Child class
@Entity
public class Child {
	
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
	
  private String name;
	
  @ManyToOne
  @JoinColumns({ 							// used to reference the parent composite key
    @JoinColumn(name="firstname_fk", referencedColumnName="firstname"),
    @JoinColumn(name="lastname_fk", referencedColumnName="lastname")
  })
  private Parent parent;

  // Constructors, Getters, Setters, etc
}