JPA Criteria API - Как получить противоположную сторону однонаправленного отношения ManyToOne от JPA - PullRequest
1 голос
/ 06 ноября 2019

Вот как выглядят мои данные:

@Entity
public class Pet {
  @Id
  protected Long id;
  protected String name;
  protected String color;
}

@Entity
public class Owner {
  @Id
  protected Long id;
@ManyToOne
@JoinColumn(name = "pet_id", referencedColumnName = "id")
  protected Pets pets;
}

Если я начну с Pet, как я могу присоединиться к владельцу. Я пытаюсь создать этот запрос:

SELECT * FROM pet p
  INNER JOIN owner o ON p.id = o.pet_id;

Я не хочу добавлять новую аннотацию к моему Pet. Я могу начать с criteriaBuilder, вот что я пытался

Root<Pet> pet = query.from(Pet.class);
Join<Pet, Owner> owners = pet.join("pet_id");

Может кто-нибудь, пожалуйста, покажите мне, как это сделать?

1 Ответ

0 голосов
/ 06 ноября 2019

Хороший подход, но у вас все немного обернулось. Многие питомцы принадлежат одному владельцу.

@Entity
@Data
public class Owner {
    @Id
    private Long id;
}

@Entity
@Data
public class Pet {
    @Id
    private Long id;
    private String name;
    private String color;
    @ManyToOne
    // this is redundant
    // @JoinColumn(name = "owner_id", referencedColumnName = "id")
    private Owner owner;

}

И самый очевидный способ их использования - это так:

tx.begin();
Owner o = new Owner();
o.setId(1L);
em.persist(o);
Pet p1 = new Pet();
p1.setId(1L);
p1.setName("Tom");
p1.setColor("Black and White");
p1.setOwner(o);
em.persist(p1);
Pet p2 = new Pet();
p2.setId(2L);
p2.setName("Jerry");
p2.setColor("Grey");
p2.setOwner(o);
em.persist(p2);
tx.commit();

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet).where(cb.equal(pet.get(Pet_.owner), 1L));
List<Pet> pets= em.createQuery(cq).getResultList();
System.out.println(pets);
List<Pet> pets2 = em.createQuery("select p from Pet p where p.owner.id = :ownerId", Pet.class).setParameter("ownerId", 1L).getResultList();
System.out.println(pets2);

или

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet).where(cb.equal(pet.get(Pet_.owner), o));
List<Pet> pets= em.createQuery(cq).getResultList();
System.out.println(pets);
List<Pet> pets2 = em.createQuery("select p from Pet p where p.owner = :owner", Pet.class).setParameter("owner", o).getResultList();
System.out.println(pets2);

и получить отжурналы:

Hibernate: create table Owner (id bigint not null, primary key (id))
Hibernate: create table Pet (id bigint not null, color varchar(255), name varchar(255), owner_id bigint, primary key (id))
Hibernate: alter table Pet add constraint FKlvivrebsqkfp4s4svpd18dqmv foreign key (owner_id) references Owner
Hibernate: insert into Owner (id) values (?)
Hibernate: insert into Pet (color, name, owner_id, id) values (?, ?, ?, ?)
Hibernate: insert into Pet (color, name, owner_id, id) values (?, ?, ?, ?)
Hibernate: select pet0_.id as id1_1_, pet0_.color as color2_1_, pet0_.name as name3_1_, pet0_.owner_id as owner_id4_1_ from Pet pet0_ where pet0_.owner_id=1
[Pet(id=1, name=Tom, color=Black and White, owner=Owner(id=1)), Pet(id=2, name=Jerry, color=Grey, owner=Owner(id=1))]
Hibernate: select pet0_.id as id1_1_, pet0_.color as color2_1_, pet0_.name as name3_1_, pet0_.owner_id as owner_id4_1_ from Pet pet0_ where pet0_.owner_id=?
[Pet(id=1, name=Tom, color=Black and White, owner=Owner(id=1)), Pet(id=2, name=Jerry, color=Grey, owner=Owner(id=1))]

Вы также можете получить противоположную сторону однонаправленного отношения ManyToOne от JPA следующим образом:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Owner> cq = cb.createQuery(Owner.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet.get(Pet_.owner)).where(cb.equal(pet, p1));
Owner o1  = em.createQuery(cq).getSingleResult();
System.out.println(o1);
Owner o2 = em.createQuery("select p.owner from Pet p where p = :pet", Owner.class).setParameter("pet", p1).getSingleResult();
System.out.println(o2);

и получить из журналов:

Hibernate: select owner1_.id as id1_0_ from Pet pet0_ inner join Owner owner1_ on pet0_.owner_id=owner1_.id where pet0_.id=?
Owner(id=1)
Hibernate: select owner1_.id as id1_0_ from Pet pet0_ inner join Owner owner1_ on pet0_.owner_id=owner1_.id where pet0_.id=?
Owner(id=1)

Конечно, вы можете не захотеть использовать предложение where, поэтому вы можете сделать:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Owner> cq = cb.createQuery(Owner.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet.get(Pet_.owner));
List<Owner> o1s  = em.createQuery(cq).getResultList();
System.out.println(o1s);
List<Owner> o2s = em.createQuery("select p.owner from Pet p", Owner.class).getResultList();
System.out.println(o2s);

или, чтобы получить то, что вы специально просили:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
pet.join(Pet_.owner);
cq.select(pet);
List<Pet> pets= em.createQuery(cq).getResultList();
System.out.println(pets);
List<Pet> pets2 = em.createQuery("select p from Pet p join p.owner", Pet.class).getResultList();
System.out.println(pets2);

и журналы будут, соответственно:

Hibernate: select owner1_.id as id1_0_ from Pet pet0_ inner join Owner owner1_ on pet0_.owner_id=owner1_.id
[Owner(id=1), Owner(id=1)]
Hibernate: select owner1_.id as id1_0_ from Pet pet0_ inner join Owner owner1_ on pet0_.owner_id=owner1_.id
[Owner(id=1), Owner(id=1)]
Hibernate: select pet0_.id as id1_1_, pet0_.color as color2_1_, pet0_.name as name3_1_, pet0_.owner_id as owner_id4_1_ from Pet pet0_ inner join Owner owner1_ on pet0_.owner_id=owner1_.id
[Pet(id=1, name=Tom, color=Black and White, owner=Owner(id=1)), Pet(id=2, name=Jerry, color=Grey, owner=Owner(id=1))]
Hibernate: select pet0_.id as id1_1_, pet0_.color as color2_1_, pet0_.name as name3_1_, pet0_.owner_id as owner_id4_1_ from Pet pet0_ inner join Owner owner1_ on pet0_.owner_id=owner1_.id
[Pet(id=1, name=Tom, color=Black and White, owner=Owner(id=1)), Pet(id=2, name=Jerry, color=Grey, owner=Owner(id=1))]

Что эквивалентно JPA:

SELECT * FROM pet p
  INNER JOIN owner o ON p.id = o.pet_id;

С foreign key в таблице pet вместотаблица owner, как указано в начале.

Имейте в виду, что вам не нужно явно выполнять соединение, поскольку JPA в любом случае автоматически получит другую сторону отношения ManyToOne. Например:

// start fresh
em.clear();
//
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
List<Pet> pets= em.createQuery(cq).getResultList();
System.out.println(pets);
List<Pet> pets2 = em.createQuery("select p from Pet p", Pet.class).getResultList();
System.out.println(pets2);

даст вам

Hibernate: select pet0_.id as id1_1_, pet0_.color as color2_1_, pet0_.name as name3_1_, pet0_.owner_id as owner_id4_1_ from Pet pet0_
Hibernate: select owner0_.id as id1_0_0_ from Owner owner0_ where owner0_.id=?
[Pet(id=1, name=Tom, color=Black and White, owner=Owner(id=1)), Pet(id=2, name=Jerry, color=Grey, owner=Owner(id=1))]
Hibernate: select pet0_.id as id1_1_, pet0_.color as color2_1_, pet0_.name as name3_1_, pet0_.owner_id as owner_id4_1_ from Pet pet0_
[Pet(id=1, name=Tom, color=Black and White, owner=Owner(id=1)), Pet(id=2, name=Jerry, color=Grey, owner=Owner(id=1))]

, который является эквивалентным запросом, выполненным с использованием двух запросов и кэширования, вместо того, чтобы всегда использовать один запрос с внутренним объединением, чтобы повторяющиеся запросы былиболее эффективный.

...