Hibernate example 4 – many to many associations

I’ve put some code on Github that shows three ways of modelling many to many associations with JPA / Hibernate:

  1. Using a join table that is not mapped as either an entity or an embedded component type.
  2. Mapping the join table as an entity in its own right, so you can add properties to it.
  3. Using a collection of embedded components, to simplify the management of the persistence of the association.

The code is available from:

https://github.com/hedleyproctor/hibernate-many-to-many

Or the direct download link for the zip is:

https://github.com/hedleyproctor/hibernate-many-to-many/archive/master.zip

I’ll explain how each of these ways of modelling the association work.

Using a join table not mapped as an entity or component

If you simply want a many to many association, without any properties attached to the association, then this is a good choice. You use a join table, and you can navigate from either end of the association. In my example, the Category and Item classes have this association, and you can see it mapped in the Category class:

	@ManyToMany(cascade = CascadeType.ALL)
	@JoinTable(
		name = "CATEGORY_ITEM",
		joinColumns = { @JoinColumn(name = "CATEGORY_ID")},
		inverseJoinColumns = {@JoinColumn(name = "ITEM_ID")}
		)
	public Set<Item> getItems() {
		return items;
	}

Since the join table isn’t an entity, it doesn’t have a corresponding Java class.

Mapping the join table as an entity in its own right

If you want to add properties to the association, then you have to map the join table as an entity. Typical examples might be the username of the person who created the association, or the time it was created. In this case, the join table is represented by a class which has a primary key composed of the two foreign keys to the entities it is linking. I won’t show all of the code here, just the start of the class:

@Entity
@Table(name = "CATEGORY_ITEM_RELATIONSHIP")
public class CategoryItemRelationship {
 
	@EmbeddedId
	private Id id = new Id();
	@ManyToOne
	@JoinColumn(name = "CATEGORY_ID", insertable = false, updatable = false)
	private Category2 category;
	@ManyToOne
	@JoinColumn(name = "ITEM_ID", insertable = false, updatable = false)
	private Item2 item;
	private Date dateAdded;
 
	@Embeddable
	public static class Id implements Serializable {
		@Column(name = "CATEGORY_ID")
		private Long categoryId;
		@Column(name = "ITEM_ID")
		private Long itemId;
 
		public Id() {}
 
		public Id(Long categoryId, Long itemId) {
			this.categoryId = categoryId;
			this.itemId = itemId;
		}

Because the id is a composite, it is represented by a static inner class and mapped using the @EmbeddedId annotation. You can see that the database columns for the category and item foreign keys are updated by this static inner Id class. Hence when the item and category instance variables in the outer class are mapped as a join, they are marked as not insertable or updatable. Similarly, when we make the association bi-directional, so that we can navigate from the Item2 and Category2 classes across the association, we have to declare that the association is mapped and controlled by the intermediate CategoryItemRelationship class. For example, in the Category2 class:

	@OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
	public Set<CategoryItemRelationship> getCategoryItemRelationships() {
		return categoryItemRelationships;
	}

Using a collection of components

This is a less common choice. When you use a component, it does not have its own lifecycle. Rather, it is owned by an entity. It also does not have an id, so you cannot retrieve it from the database using an id. (Obviously in database terms a component stored in its own table will have a primary key, but this is never represented in the component class as an id field.) Finally, because a component can only have one owning entity (since its lifecycle is tied to the owning entity), you cannot enable bidirectionality on an association mapped using components. However, using a component won’t require configuring any cascade settings, so could be an appropriate choice if you have an association that you think will generally be accessed and administered from one side. In my example, this component is the CategorizedItem class:

@Embeddable
public class CategorizedItem {
	private String username;
	private Date dateAdded = new Date();
	private Item3 item;
	private Category3 category;
	public CategorizedItem(String username,
		Category3 category,
		Item3 item) {
		this.username = username;
		this.category = category;
		this.item = item;
	}
...

You can see that because it is a component rather than an entity, it is given the @Embedded annotation. Since it does not need an identifier, it doesn’t require the static inner Id class used by the entity approach. At a database level, the primary key of the categorized item table will be a composite of all columns. To link the Category3 class to the CategorizedItem, we use the @ElementCollection and @CollectionTable annotations:

@Entity
public class Category3 {
	private Long id;
	private String name;
	private Set<CategorizedItem> categorizedItems = new HashSet<CategorizedItem>();
 
	@ElementCollection
	@CollectionTable(name = "CATEGORIZED_ITEMS", joinColumns = @JoinColumn(name = "CATEGORY_ID"))
	public Set<CategorizedItem> getCategorizedItems() {
		return categorizedItems;
	}
	public void setCategorizedItems(Set<CategorizedItem> categorizedItems) {
		this.categorizedItems = categorizedItems;
	}
 
...
This entry was posted in Hibernate, Java and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

501,962 Spambots Blocked by Simple Comments

HTML tags are not allowed.