I have a legacy DB and i want to select an entity with children. But, i dont have foreign key and I join on “natural key”. But when I select an entity A with entities B hibernate does second select for A. NaturalId doesn’t help and with normal join (on keys) there is only one select.
Its a bug or no? Its look like hibernate doesn’t recognize that entity A its already fetch.
Thx for help @Naros on HipChat, but we didnt resolve problem.
Maybe @vlad
Thanks mikaelst
selects:
Hibernate:
select
jpaunittes0_.id as id1_0_0_,
set1_.id as id1_1_1_,
jpaunittes0_.otherId as otherId2_0_0_,
set1_.otherId as otherId2_1_1_,
set1_.otherId as otherId2_1_0__,
set1_.id as id1_1_0__
from
A jpaunittes0_
left outer join
B set1_
on jpaunittes0_.otherId=set1_.otherId
Hibernate:
select
jpaunittes0_.id as id1_0_0_,
jpaunittes0_.otherId as otherId2_0_0_
from
A jpaunittes0_
where
jpaunittes0_.otherId=?
Code:
package org.hibernate.bugs;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* This template demonstrates how to develop cos test case for Hibernate ORM, using the Java Persistence API.
*/
public class JPAUnitTestCase {
private EntityManagerFactory entityManagerFactory;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory("templatePU");
}
@After
public void destroy() {
entityManagerFactory.close();
}
// Entities are auto-discovered, so just add them anywhere on class-path
// Add your tests, using standard JUnit.
@Test
public void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
final A a = new A(123L);
entityManager.persist(a);
entityManager.persist(new B(a));
entityManager.persist(new B(a));
entityManager.getTransaction().commit();
entityManager.close();
EntityManager em = entityManagerFactory.createEntityManager();
em.createQuery("select a from A a left join fetch a.set b")
.getResultList();
entityManager.close();
}
@Entity(name = "A")
@Access(AccessType.PROPERTY)
public static class A implements Serializable {
private Long id;
private Long otherId;
private Set<B> set = new HashSet<>(0);
A() {
}
public A(final Long otherId) {
this.otherId = otherId;
}
@Id
@GeneratedValue
private Long getId() {
return id;
}
private void setId(final Long id) {
this.id = id;
}
private Long getOtherId() {
return otherId;
}
private void setOtherId(final Long otherId) {
this.otherId = otherId;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "cos")
private Set<B> getSet() {
return set;
}
private void setSet(final Set<B> set) {
this.set = set;
}
}
@Entity(name = "B")
@Access(AccessType.PROPERTY)
public static class B implements Serializable {
private Long id;
private A cos;
B() {
}
public B(final A cos) {
this.cos = cos;
}
@Id
@GeneratedValue
private Long getId() {
return id;
}
private void setId(final Long id) {
this.id = id;
}
@ManyToOne(optional = false)
@JoinColumn(name = "otherId", referencedColumnName = "otherId", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private A getCos() {
return cos;
}
private void setCos(final A cos) {
this.cos = cos;
}
}
}
Hibernate:
select
jpaunittes0_.id as id1_0_0_,
set1_.id as id1_1_1_,
jpaunittes0_.otherId as otherId2_0_0_,
set1_.otherId as otherId2_1_1_,
set1_.otherId as otherId2_1_0__,
set1_.id as id1_1_0__
from
A jpaunittes0_
left outer join
B set1_
on jpaunittes0_.otherId=set1_.otherId
Hibernate:
select
jpaunittes0_.id as id1_0_0_,
jpaunittes0_.otherId as otherId2_0_0_
from
A jpaunittes0_
where
jpaunittes0_.otherId=?
Code:
package org.hibernate.bugs;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* This template demonstrates how to develop cos test case for Hibernate ORM, using the Java Persistence API.
*/
public class JPAUnitTestCase {
private EntityManagerFactory entityManagerFactory;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory("templatePU");
}
@After
public void destroy() {
entityManagerFactory.close();
}
// Entities are auto-discovered, so just add them anywhere on class-path
// Add your tests, using standard JUnit.
@Test
public void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
final A a = new A(123L);
entityManager.persist(a);
entityManager.persist(new B(a));
entityManager.persist(new B(a));
entityManager.getTransaction().commit();
entityManager.close();
EntityManager em = entityManagerFactory.createEntityManager();
em.createQuery("select a from A a left join fetch a.set b")
.getResultList();
entityManager.close();
}
@Entity(name = "A")
@Access(AccessType.PROPERTY)
public static class A implements Serializable {
private Long id;
private Long otherId;
private Set<B> set = new HashSet<>(0);
A() {
}
public A(final Long otherId) {
this.otherId = otherId;
}
@Id
@GeneratedValue
private Long getId() {
return id;
}
private void setId(final Long id) {
this.id = id;
}
private Long getOtherId() {
return otherId;
}
private void setOtherId(final Long otherId) {
this.otherId = otherId;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "cos")
private Set<B> getSet() {
return set;
}
private void setSet(final Set<B> set) {
this.set = set;
}
}
@Entity(name = "B")
@Access(AccessType.PROPERTY)
public static class B implements Serializable {
private Long id;
private A cos;
B() {
}
public B(final A cos) {
this.cos = cos;
}
@Id
@GeneratedValue
private Long getId() {
return id;
}
private void setId(final Long id) {
this.id = id;
}
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "otherId", referencedColumnName = "otherId", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private A getCos() {
return cos;
}
private void setCos(final A cos) {
this.cos = cos;
}
}
}
DEBUG [Alice]: n.t.d.l.SLF4JQueryLoggingListener - Name:DATA_SOURCE_PROXY, Time:0, Success:True, Type:Prepared, Batch:False, QuerySize:1, BatchSize:0, Query:["select manytoonej0_.id as id1_1_0_, manytoonej1_.id as id1_0_1_, manytoonej0_.isbn as isbn5_1_0_, manytoonej0_.currency as currency2_1_0_, manytoonej0_.price_in_cents as price_in3_1_0_, manytoonej0_.publisher as publishe4_1_0_, manytoonej1_.author as author2_0_1_, manytoonej1_.isbn as isbn3_0_1_, manytoonej1_.title as title4_0_1_ from publication manytoonej0_ inner join book manytoonej1_ on manytoonej0_.isbn=manytoonej1_.isbn where manytoonej1_.isbn=? and manytoonej0_.currency=?"], Params:[(978-9730228236, &)]
So, there’s no bug. Just compare my test with yours and see where they differ.
First of all, if hibernate can’t handle same outputs from queries where queries differs only in join on clause, its a bug (for me).
pk join:
select
jpaunittes0_.id as id1_0_0_,
set1_.id as id1_1_1_,
jpaunittes0_.otherId as otherId2_0_0_,
set1_.otherId as otherId2_1_1_,
set1_.otherId as otherId2_1_0__,
set1_.id as id1_1_0__
from
A jpaunittes0_
left outer join
B set1_
on jpaunittes0_.id=set1_.otherId
non-pk join:
select
jpaunittes0_.id as id1_0_0_,
set1_.id as id1_1_1_,
jpaunittes0_.otherId as otherId2_0_0_,
set1_.otherId as otherId2_1_1_,
set1_.otherId as otherId2_1_0__,
set1_.id as id1_1_0__
from
A jpaunittes0_
left outer join
B set1_
on jpaunittes0_.otherId=set1_.otherId
Hibernate:
select
jpaunittes0_.id as id1_0_0_,
jpaunittes0_.otherId as otherId2_0_0_
from
A jpaunittes0_
where
jpaunittes0_.otherId=?
Secondly, I check your test case and it is different. You don’t have OneToMany so for me it is different scenario and cant be compared or I am wrong.
At the end, rly, rly, thanks for responses. Great job guys, I am impressed how fast I get feedback on hipchat and forum