Persist further attributes for associations with JPA & Hibernate

[ad_1]

JPA and Hibernate let you outline associations between entities with only a few annotations, and also you don’t need to care concerning the underlying desk mannequin within the database. Even be part of tables for many-to-many associations are hidden behind a @JoinTable annotation, and also you don’t have to mannequin the extra desk as an entity.

That modifications as quickly as you must persist further attributes of an affiliation. The apparent strategy to deal with that’s to create an entity for the be part of desk and add the attributes there. However that instantly will get you to the following query: How do you mannequin a major key that consists of two international keys?

That’s simpler than you may assume.

Mannequin

You understand the instance for this publish from a typical bookstore. There are books in a number of codecs (e.g., hardcover, paperback, e-book), and every format was printed by a special writer.

You’ll be able to mannequin that with 3 entity lessons. The E-book and Writer entities are fairly apparent and mannequin the 2 predominant area objects. The third one is the BookPublisher entity which fashions the affiliation between the E-book and the Writer and retains the Format as an extra attribute.

OK, in case you have some expertise with database modeling, you most likely anticipated such an entity mannequin. It’s fairly near the desk mannequin and never too troublesome.

Let’s take a more in-depth have a look at the mapping definition.

The E-book and Writer entities

There may be nothing too fascinating concerning the E-book and the Writer entity. Each of them outline a one-to-many affiliation with the BookPublisher entity. The fascinating components of the mapping are within the BookPublisher entity, which I’ll present you within the subsequent part.

@Entity
public class E-book {

	@Id
	@GeneratedValue(technique = GenerationType.SEQUENCE)
	non-public Lengthy id;

	@Model
	non-public int model;

	non-public String title;

	@OneToMany(mappedBy = "ebook")
	non-public Set<BookPublisher> publishers = new HashSet<BookPublisher>();
	
	...
	
}
@Entity
public class Writer {

	@Id
	@GeneratedValue(technique = GenerationType.SEQUENCE)
	non-public Lengthy id;

	@Model
	non-public int model;

	non-public String identify;

	@OneToMany(mappedBy = "writer")
	non-public Set<BookPublisher> books = new HashSet<BookPublisher>();
	
	...
	
}

The BookPublisher entity

OK, I promised you that the mapping of the BookPublisher entity is extra fascinating than those I confirmed you earlier than. However that doesn’t imply that it’s extra advanced.

As you have got seen within the diagram, the BookPublisher entity maps the affiliation between the E-book and the Writer entities and shops the format of the ebook as an extra attribute. At first look, the required mapping might sound simple. You solely want 2 many-to-one associations and the extra attribute.

However what concerning the major key?

As you have got seen within the diagram, the BookPublisher entity makes use of the mixture of the international key of the E-book entity and the international key of the Writer entity as the first key. Each of them are hidden by the many-to-one affiliation mapping.

The best choice to map such a composite major secret’s to outline an @Embeddable that represents it. On this instance, I created the BookPublisherId class. It’s 2 attributes of kind Lengthy signify the two components of the first key.

@Entity
public class BookPublisher {

	@Embeddable
	public static class BookPublisherId implements Serializable {

		protected Lengthy bookId;

		protected Lengthy publisherId;

		public BookPublisherId() {
			
		}
		
		public BookPublisherId(Lengthy bookId, Lengthy publisherId) {
			this.bookId = bookId;
			this.publisherId = publisherId;
		}

		@Override
		public int hashCode() {
			closing int prime = 31;
			int end result = 1;
			end result = prime * end result
					+ ((bookId == null) ? 0 : bookId.hashCode());
			end result = prime * end result
					+ ((publisherId == null) ? 0 : publisherId.hashCode());
			return end result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			
			BookPublisherId different = (BookPublisherId) obj;
			
			if (bookId == null) {
				if (different.bookId != null)
					return false;
			} else if (!bookId.equals(different.bookId))
				return false;
			
			if (publisherId == null) {
				if (different.publisherId != null)
					return false;
			} else if (!publisherId.equals(different.publisherId))
				return false;
			
			return true;
		}
	}
	
	...
	
}

As you may see, the mapping of the BookPublisherId is straightforward.

It’s a must to annotate the category with an @Embeddable annotation. It’s now a normal embeddable that you need to use at an attribute in all of your entity lessons.

If you wish to use an embeddable object as a major key, there are 2 extra issues it’s good to do:

  1. Your class must implement the Serializable interface.
  2. It’s essential to implement the hashCode and equals strategies.

That’s all it’s good to do to outline an embeddable that may signify a major key. Now you can use it as an attribute kind and annotate it with @EmbeddedId.

Let’s check out the BookPublisher mapping subsequent.

@Entity
public class BookPublisher {
  
	@EmbeddedId
	non-public BookPublisherId id;

	@ManyToOne
	@JoinColumn(identify = "bookid")
	@MapsId("bookId")
	non-public E-book ebook;

	@ManyToOne
	@JoinColumn(identify = "publisherid")
	@MapsId("publisherId")
	non-public Writer writer;

	@Enumerated(EnumType.STRING)
	non-public Format format;
	
	...
	
}

As you may see within the code snippet, the id attribute is of kind BookPublisherId, and I annotated it with @EmbeddedId. That tells Hibernate to make use of the BookPublisherId class as the first key class and use its mapping definition to map the attributes to the database columns.

Within the following traces, you may see the mapping definition of the two many-to-one associations to the E-book and Writer entities. These present the international keys that kind the first key of every BookPublisher entity object.

You’ll be able to annotate them with a @MapsId annotations to inform Hibernate to make use of the first keys of the referenced E-book and Writer entities as components of the first key of the BookPublisher entity. The supplied Strings reference the corresponding attributes of the BookPublisherId object.

That’s all it’s good to do to outline the mapping. Hibernate will now handle the first key of all BookPublisher entities routinely based mostly on the first keys of the two related entities.

How you can use the mapping

You should utilize the BookPublisher entity in the identical method as another entity. The one factor you want to remember is that it’s good to set the associations to the E-book and the Writer entity earlier than persisting a brand new BookPublisher object.

EntityManager em = emf.createEntityManager();
em.getTransaction().start();

E-book b = new E-book();
b.setTitle("My E-book");
em.persist(b);

Writer p = new Writer();
p.setName("My Writer");
em.persist(p);

BookPublisher bp = new BookPublisher();
bp.setBook(b);
bp.setPublisher(p);
bp.setFormat(Format.EBOOK);
em.persist(bp);

em.getTransaction().commit();
em.shut();

Abstract

Hibernate’s customary mapping of a many-to-many affiliation hides the mapping desk. Resulting from that, you may’t use it in case your affiliation desk consists of further columns.

In that case, it’s good to add an entity class that maps the affiliation desk and break up the many-to-many affiliation mapping into 2 many-to-one associations. That entity can then map the extra columns of the affiliation desk and normally makes use of a composite major key that consists of the two international keys to the related database tables.

One of the best ways to map such a composite secret’s to outline an @Embeddable with 2 attributes and annotate each associations with a @MapsId annotation.

[ad_2]

Leave a Comment

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