нет атрибута @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 .
- Закройте
EntityManager
или Session
после . Вы закончили с A
объектами и больше их не используете.Если вы позвоните b.as
до закрытия em
, Hibernate (или любой другой поставщик сохраняемости) будет лениво загружать A
объектов из базы данных. - В
@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 + "}";
}
}