01 October 2020
Some time you may need to keep reference by class and reference by ID for your JPA @Entity. It can be very helpful, for example, to do some default JSON serialization with no risk to stuck with well known N+1 issue. In this case i would like to avoid @OneToMany and @ManyToOne fields serialization by default and use ID reference instead.
So, below is simple example how to do above.
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "name")
private String name;
@JoinColumn(name = "author", insertable = false, updatable = false)
@ManyToOne(targetEntity = Author.class, fetch = FetchType.LAZY)
private Author author;
@Column(name = "author")
private long authorId;
...
public void setAuthor(Author author) {
setAuthorId(author.getId());
this.author = author;
}
...
}
Testing time:
public class ManyToOneTest {
@Test
public void relationManyToOneTest() {
EntityManager em = Persistence.createEntityManagerFactory("myDSTest").createEntityManager();
this.generateTestData(em);
List <Book> books =em.createQuery("FROM Book", Book.class).getResultList();
// lazy loading test
for (Book b : books) {
System.out.println("Bookd:" + b.getName());
System.out.println("AuthorId:" + b.getAuthorId());
// lazy load
System.out.println("Author:" + b.getAuthor());
}
// JPQL with direct id reference
books = em.createQuery("FROM Book where authorId = 1", Book.class).getResultList();
// JPQL with author.id reference
books = em.createQuery("FROM Book where author.id = 1", Book.class).getResultList();
}
private void generateTestData(EntityManager em) {
em.getTransaction().begin();
Author author = new Author();
author.setName("A Name");
em.persist(author);
Book book = new Book();
book.setName("Book Name");
book.setAuthorId(author.getId());
//book.setAuthor(author);
em.persist(book);
em.getTransaction().commit();
em.clear();
}
}
Test Output:
...
Hibernate: select book0_.id as id1_2_, book0_.author as author2_2_, book0_.name as name3_2_ from book book0_
Bookd:Book Name
AuthorId:1
# call book.getAuthor():
Hibernate: select author0_.id as id1_0_0_, author0_.name as name2_0_0_ from author author0_ where author0_.id=?
Author:Author{id=1, name=A Name}
PS: book.getAuthor().getId()
will not trigger Author lazy loading, but book.getAuthor().getName() will.
Source code of described application available on GitHub