JPA QL для выбора не принадлежащей стороны отношений ManytoMany? - PullRequest
0 голосов
/ 20 декабря 2011

У меня есть отношения ManytoMany между A и B, где A является стороной-владельцем.Я определяю ManyToMany в классе A:

@ManyToMany(....)
private Set<B> bs

Но я не хочу выставлять set в B, поэтому в B не определен атрибут @ManyToMany (например, Set as).Это связано с проблемой, когда я хочу выбрать все объекты B, которым сопоставлен экземпляр A с использованием JPA QL.Я не могу сделать:

"SELECT b FROM B b JOIN b.as A WHERE A.id = :id"

Я мог бы установить fetch = Fetch.EAGER в свойствах @ManyToMany и использовать A.getBs (), чтобы получить связанный B. Но я предпочитаю не использовать Fetch.EAGER.Любое предложение?Спасибо

1 Ответ

1 голос
/ 20 декабря 2011

нет атрибута @ManyToMany, определенного в B (например, Установить как)

(Я должен был исправить себя, потому что кажется, что он пропускает Set<A> as из B вообще выигралне создавать исключение.)

Если вы хотите только скрыть Set<A> as в B, то вы можете объявить его как private и использовать двунаправленное отображение (используяmappedBy свойство на не принадлежащей стороне отношения).В этом случае следующий запрос выполняется успешно:

EntityManager em = ...
String sql = "SELECT b FROM B b JOIN b.as a WHERE a.id = :id";
TypedQuery<B> tq = em.createQuery(sql, B.class);
tq.setParameter("id", 100);
for (B b : tq.getResultList())
  System.out.println(b);

(Все примеры кода основаны на таблицах, данных и сущностях, найденных в нижних разделах ответа.)

Он печатает:

B{id=333, data=b}
B{id=999, data=bbb}

Этот JPQL-запрос является отображением следующего собственного SQL-запроса:

SELECT b.id, b.data 
FROM a, b, a_has_b 
WHERE a.id = a_has_b.a_id 
AND b.id = a_has_b.b_id 
AND a_id = 100;

В основном вы хотите однонаправленное отношение (опускаяmappedBy от B - внизу или Set<A> as).Таким образом, однако, вы не сможете выполнить запрос, как вы описали.Просто никак не получится.

Поставщик персистентности будет лаять на вас, если в B нет Set<A> as (свойство не может быть разрешено - в случае Hibernate).Если вы пропустите только mappedBy со стороны, не являющейся владельцем, тогда поставщик постоянных данных не будет знать, где находится другая сторона , которая .Либо вы используете mappedBy, либо создаете аннотацию inverse @JoinTable в B тоже (mappedBy там, чтобы вам не пришлось последний).

Если у вас есть только однонаправленное отображение от A к B, вы можете только извлечь сущность A по ее идентификатору и найти все связанные с ней B-сущности, как это (именно то, что вы описали):

TypedQuery<A> tq = em.createQuery("SELECT a FROM A a WHERE id = :id", A.class);
tq.setParameter("id", 100);
for (A a : tq.getResultList())
  for (B b : a.bs)
    System.out.println(b);

Это работает для меня без указания fetch = Fetch.EAGER и печатает те же материалы, что и раньше.

Помните, что если действует Fetch.LAZY, вы получите ошибки, если попытаетесь получить ленивый доступзагруженные объекты после закрытия EntityManager или (Hibernate) Session.Вы ничего не можете сделать с этим: это способ, которым он должен работать.

EntityManager em = ...
// fetch your B instances
List<B> bs = ...
em.close();
for (B b : bs)
  for (A a : b.as)
    // *BOOM*
    System.out.println(a);

Вы можете сделать две вещи, чтобы предотвратить BOOM .

  1. Закройте EntityManager или Session после . Вы закончили с A объектами и больше их не используете.Если вы позвоните b.as до закрытия em, Hibernate (или любой другой поставщик сохраняемости) будет лениво загружать A объектов из базы данных.
  2. В @ManyToMany аннотации *1082* вашего объекта fetch* до FetchType.EAGER.Таким образом, когда вы выбираете B объекты из базы данных, их свойство Set<A> as также будет загружаться Hiberate (дальнейшее управление может быть осуществлено с другими настройками CascadeType - я думаю).

Я предлагаю вместо этого использовать двунаправленное отображение (не пропускайте mappedBy) или сделать B сторону-владельцем (но первая будет очень полезной).

Таблицы

test.a

+-------+-------------+------+-----+---------+
| Field | Type        | Null | Key | Default |
+-------+-------------+------+-----+---------+
| id    | int(11)     | NO   | PRI | 0       |
| data  | varchar(45) | YES  |     | NULL    |
+-------+-------------+------+-----+---------+

test.b

+-------+-------------+------+-----+---------+
| Field | Type        | Null | Key | Default |
+-------+-------------+------+-----+---------+
| id    | int(11)     | NO   | PRI | 0       |
| data  | varchar(45) | YES  |     | NULL    |
+-------+-------------+------+-----+---------+

test.a_has_b

+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a_id  | int(11) | NO   | PRI | 0       |       |
| b_id  | int(11) | NO   | PRI | 0       |       |
+-------+---------+------+-----+---------+-------+

Данные

test.a

+-----+------+
| id  | data |
+-----+------+
| 100 | a    |
| 200 | aa   |
| 300 | aaa  |
+-----+------+

test.b

+-----+------+
| id  | data |
+-----+------+
| 333 | b    |
| 666 | bb   |
| 999 | bbb  |
+-----+------+

test.a_has_b

+------+------+
| a_id | b_id |
+------+------+
|  100 |  333 |
|  300 |  333 |
|  100 |  999 |
+------+------+

Сущности

A

@Entity
@Table(schema = "test", name = "a")
public final class A {

  @Id
  public int id;

  @Basic
  public String data;

  @ManyToMany(targetEntity = B.class,
              cascade = CascadeType.ALL,
              fetch = FetchType.LAZY)
  @JoinTable(schema = "test",
             name = "a_has_b",
             joinColumns = @JoinColumn(table = "a",
                                       name = "a_id",
                                       referencedColumnName = "id"),
             inverseJoinColumns = @JoinColumn(table = "b",
                                              name = "b_id",
                                              referencedColumnName = "id"))
  public Set<B> bs = Sets.newLinkedHashSet();

  @Override
  public String toString() {
    return "A{id=" + id + ", data=" + data + "}";
  }
}

B

@Entity
@Table(schema = "test", name = "b")
public final class B {

  @Id
  public int id;

  @Basic
  public String data;

  // omitting mappedBy results in a uni-directional relationship
  @ManyToMany(targetEntity = A.class,
              cascade = CascadeType.ALL,
              fetch = FetchType.LAZY,
              mappedBy = "bs")
  public Set<A> as = Sets.newLinkedHashSet();

  @Override
  public String toString() {
    return "B{id=" + id + ", data=" + data + "}";
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...