Hibernate Tip: How to customize the association mappings using a composite key

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.

Question:

“One of my entities uses an @IdentityClass to map a composite primary key, and I need to map a one-to-many association to it. How can I change the names of the foreign key columns?”

Solution:

If you don’t provide any additional information about your association mapping, Hibernate uses default names for the foreign key columns. They follow the pattern <association attribute name>_<primary key attribute name>. That means that books_id is the default name for an association that’s mapped by the books attribute, and that references an entity with a primary key column called id.

This approach is independent of the number of columns that form the primary key of the referenced entity. If it’s a composite primary key, Hibernate generates a default name for each of the identifying entity attributes.

Customize the default column names

You can adapt this mapping using one or more @JoinColumn annotations. Since Hibernate 5 and JPA 2.2, the @JoinColumn annotation is repeatable, and you can apply multiple of them to your association mapping attribute. If you’re using an older Hibernate version, you need a @JoinColumn annotation for each column and wrap them in a @JoinColumns annotation.

Let’s take a look at a simple example.

A Product gets identified by the combination of the manufacturerId and a manufacturer-specific productId. As requested in the question, I model the primary key using an @IdClass. If you want to learn more about this primary key mapping or the mapping of composite primary keys in general, please take a look at my Advanced Hibernate Online Training.

@Entity
@IdClass(value = Product.ProductId.class)
public class Product {

	@Id
	private Long productId;

	@Id
	private String manufacturerId;

	@Version
	private int version;

	private String title;

	// getter and setter methods omitted

	public static class ProductId implements Serializable {
		
		private Long productId;
	
		private String manufacturerId;

		public ProductId() {
			super();
		}

		public ProductId(Long productId, String manufacturerId) {
			this.productId = productId;
			this.manufacturerId = manufacturerId;
		}

		// getter and setter methods omitted

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((manufacturerId == null) ? 0 : manufacturerId.hashCode());
			result = prime * result + ((productId == null) ? 0 : productId.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ProductId other = (ProductId) obj;
			if (manufacturerId == null) {
				if (other.manufacturerId != null)
					return false;
			} else if (!manufacturerId.equals(other.manufacturerId))
				return false;
			if (productId == null) {
				if (other.productId != null)
					return false;
			} else if (!productId.equals(other.productId))
				return false;
			return true;
		}
	}
}

The vital part of this Hibernate Tip is the mapping of the Review entity.

@Entity
public class Review {

	@Id
	@GeneratedValue
	private Long id;

	@ManyToOne
	@JoinColumn(name = "p_id", referencedColumnName = "productid")
	@JoinColumn(name = "m_id", referencedColumnName = "manufacturerid")
	private Product product;

	private String comment;

	// getter and setter methods omitted
}

The product attribute maps the association to the Product entity. Hibernate’s default mapping would expect that the columns product_productid and product_manufacturerid on the review table store the foreign key to a record in the product table.

As described earlier, you can change this mapping by annotating the product attribute with one or more @JoinColumn annotations. I did that in the code snippet to tell Hibernate to use the columns p_id and m_id as the foreign key references to the productid and manufacturerid columns on the product table.

When I use this entity to persist a new record, you can see that Hibernate uses the column names p_id and m_id instead of the default names.

17:56:46,584 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Review
        (comment, m_id, p_id, id) 
    values
        (?, ?, ?, ?)

Learn more:

If you want to learn more about primary key and association mappings, please read the following articles:

 

Hibernate Tips Book







Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching, and statically and dynamically defined queries.

Get it now!


Related Articles

Responses

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.