Sunday, June 23, 2024
HomeJavaPersist further attributes for associations with JPA & Hibernate

Persist further attributes for associations with JPA & Hibernate


JPA and Hibernate mean you can outline associations between entities with just some annotations, and also you don’t need to care concerning the underlying desk mannequin within the database. Even be a 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 plain method to deal with that’s to create an entity for the be a part of desk and add the attributes there. However that instantly will get you to the subsequent query: How do you mannequin a main key that consists of two international keys?

That’s simpler than you may suppose.

Mannequin

You recognize the instance for this submit 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 Ebook and Writer entities are fairly apparent and mannequin the 2 most important area objects. The third one is the BookPublisher entity which fashions the affiliation between the Ebook and the Writer and retains the Format as a further attribute.

OK, if 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 tough.

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

The Ebook and Writer entities

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

@Entity
public class Ebook {

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

	@Model
	non-public int model;

	non-public String title;

	@OneToMany(mappedBy = "guide")
	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 complicated.

As you have got seen within the diagram, the BookPublisher entity maps the affiliation between the Ebook and the Writer entities and shops the format of the guide as a further attribute. At first look, the required mapping may appear simple. You solely want 2 many-to-one associations and the extra attribute.

However what concerning the main key?

As you have got seen within the diagram, the BookPublisher entity makes use of the mix of the international key of the Ebook 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 most suitable choice to map such a composite main secret’s to outline an @Embeddable that represents it. On this instance, I created the BookPublisherId class. It’s 2 attributes of sort Lengthy symbolize the two elements 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() {
			last int prime = 31;
			int consequence = 1;
			consequence = prime * consequence
					+ ((bookId == null) ? 0 : bookId.hashCode());
			consequence = prime * consequence
					+ ((publisherId == null) ? 0 : publisherId.hashCode());
			return consequence;
		}

		@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 possibly can see, the mapping of the BookPublisherId is easy.

You must annotate the category with an @Embeddable annotation. It’s now a regular 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 main key, there are 2 extra issues you want to do:

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

That’s all you want to do to outline an embeddable that may symbolize a main key. Now you can use it as an attribute sort 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 Ebook guide;

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

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

As you possibly can see within the code snippet, the id attribute is of sort 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 strains, you possibly can see the mapping definition of the two many-to-one associations to the Ebook 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 Ebook and Writer entities as elements of the first key of the BookPublisher entity. The offered Strings reference the corresponding attributes of the BookPublisherId object.

That’s all you want to do to outline the mapping. Hibernate will now handle the first key of all BookPublisher entities mechanically primarily based on the first keys of the two related entities.

The way to use the mapping

You need to use the BookPublisher entity in the identical manner as some other entity. The one factor you want to bear in mind is that you want to set the associations to the Ebook and the Writer entity earlier than persisting a brand new BookPublisher object.

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

Ebook b = new Ebook();
b.setTitle("My Ebook");
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 commonplace mapping of a many-to-many affiliation hides the mapping desk. Attributable to that, you possibly can’t use it in case your affiliation desk contains further columns.

In that case, you want to add an entity class that maps the affiliation desk and cut 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 often makes use of a composite main key that consists of the two international keys to the related database tables.

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

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments