简体   繁体   English

JPA QL用于选择ManytoMany关系的非所有权方吗?

[英]JPA QL for selecting non-owning side of ManytoMany relationship?

I have a ManytoMany relationship between A and B, where A is the owning side. 我在A和B之间有一个ManytoMany关系,其中A是拥有方。 I define the ManyToMany in class A: 我在A类中定义ManyToMany:

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

But I don't want to expose set in B, so no @ManyToMany attribute defined in B (eg Set as). 但是我不想在B中公开set,所以在B中没有定义@ManyToMany属性(例如Set as)。 It stems a problem when I want to select all B entity which an A instance is mapped to using JPA QL. 当我要选择使用JPA QL将A实例映射到的所有B实体时,这会产生一个问题。 I can not do: 我不能做:

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

I could have set fetch = Fetch.EAGER in the @ManyToMany properties and use A.getBs() to get related B. But I prefer not to use Fetch.EAGER. 我本可以在@ManyToMany属性中设置fetch = Fetch.EAGER并使用A.getBs()来获取相关的B。但是我不想使用Fetch.EAGER。 Any suggestion? 有什么建议吗? Thanks 谢谢

no @ManyToMany attribute defined in B (eg Set as) B中未定义@ManyToMany属性(例如,设置为)

(I had to correct myself, because it seems omitting Set<A> as from B altogether won't raise an exception.) (我不得不纠正自己,因为似乎省略了Set<A> asB来看这完全不会引发异常。)

If you only want to hide the Set<A> as in B , then you can declare it as private and use a bi-directional mapping (using the mappedBy property on the non-owning side of the relation). 如果只想像B Set<A> as隐藏Set<A> as ,则可以将其声明为private并使用双向映射(使用关系的非所有权侧的mappedBy属性)。 In this case the following query runs successfully: 在这种情况下,以下查询将成功运行:

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);

(The example snippets are all based on the tables, data and entities found in the lower sections of the answer.) (示例代码段均基于答案下部的表,数据和实体。)

It prints: 它打印:

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

This JPQL query is the mapping of the following native SQL query: 此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;

You basically want a uni-directional relation (by omitting the mappedBy from B —down below—or droppping Set<A> as ). 你基本上要一个单向关系(通过省略mappedByB -向下低于或droppping Set<A> as )。 This way, however, you won't be able to execute a query like you've described. 但是,这样一来,您将无法执行如上所述的查询。 Just no way it'll work. 根本不可能。

The persistence provider will bark at you if there is no Set<A> as in B ( property could not be resolved —in case of Hibernate). 如果没有Set<A> as B Set<A> as ,持久性提供程序将向您Set<A> as无法解析属性 -如果是Hibernate)。 If you only omit the mappedBy from the non-owning side, then the persistence provider won't know where is the other side of that relation. 如果仅从非所有者方省略了mappedBy ,则持久性提供程序将不知道关系的另一方在哪里。 Either you use the mappedBy or create an inverse @JoinTable annotation in B too ( mappedBy is there so that you don't have to the latter). 无论您使用mappedBy或建立 @JoinTable在注释B太( mappedBy有没有让你不必后者)。

If you only have a uni-directional mapping from A towards B you can only fetch an A entity by its id and find all B entities that are associated with it, like this (just what you've described): 如果只有从AB的单向映射,则只能按其ID获取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);

This works for me without specifying fetch = Fetch.EAGER and prints the same stuff as before. 这对我有效,而无需指定fetch = Fetch.EAGER并且输出与以前相同的内容。

Beware that if Fetch.LAZY is in effect you'll receive errors if you try accessing lazily loaded entities after closing an EntityManager or (Hibernate) Session . 请注意,如果Fetch.LAZY有效,则在关闭EntityManager或(Hibernate) Session后尝试访问延迟加载的实体时,将会收到错误消息。 You can't do anything about this: this is the way it's supposed to work. 您对此无能为力:这就是它应该起作用的方式。

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);

You can do two things to prevent BOOM from happening. 您可以做两件事来防止BOOM发生。

  1. Close your EntityManager or Session after you're done with your A objects and don't use them anymore. 完成A对象后,请关闭EntityManagerSession 并且不再使用它们。 If you call b.as before em gets closed Hibernate (or any other persistence provider) will load A objects lazily from the database. 如果在em关闭之前调用b.as ,则Hibernate(或任何其他持久性提供程序)将从数据库中延迟加载A对象。
  2. On your B entity's @ManyToMany annotation change fetch to FetchType.EAGER . B实体的@ManyToMany批注上,将fetch更改为FetchType.EAGER This way, when you fetch B objects from the database their Set<A> as property will be loaded by Hiberate too (further control can be practiced with different CascadeType settings—I think). 这样,当您从数据库中获取B对象时,它们的Set<A> as属性也将由Hiberate加载(我可以用不同的CascadeType设置来实践进一步的控制)。

I propose that you use a bi-directional mapping instead (don't omit mappedBy ) or make B the owning side (but the former would be much useful). 我建议您改用双向映射(不要忽略mappedBy )或将B当作拥有方(但前者会很有用)。

Tables 桌子

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 test.a_has_b

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

Data 数据

test.a 测试

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

test.b 测试

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

test.a_has_b test.a_has_b

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

Entities 实体

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 + "}";
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 @OneToOne/@ManyToOne/@ManyToMany 的非拥有实体端 - Non-owning entity side of @OneToOne/@ManyToOne/@ManyToMany 在Hibernate @ManyToMany双向映射中保留非所有者副对象不会在联接表中保留数据 - Persisting non-owning side object in Hibernate @ManyToMany bidirectional mapping does not persist data in joining table 使用非主端上的主/外键映射来映射JPA双向@OneToOne - Mapping a JPA bi-directional @OneToOne with primary/foreign key mapping on non-owning side 拥有侧与非拥有侧在休眠中及其在按元素映射的引用中的用法? - Owning Side vs Non-Owning Side In hibernate and its usage in reference of mapped by element? Hibernate ManyToMany映射更新非拥有方 - Hibernate ManyToMany mapping updating the non owning side 从Hibernate中的非拥有实体查询拥有实体 - Querying the owning entity from the non-owning entity in Hibernate JPA:哪一方应该是am:n关系的拥有方? - JPA: which side should be the owning side in a m:n relationship? 添加到@ManyToMany的拥有方集合时,避免选择所有行的最佳实践 - Best pratice to avoid selecting all rows when adding to owning side collection of a @ManyToMany 为什么我们总是坚持JPA关系的拥有方? 为什么我们不坚持关系的另一面 - Why we always persist the owning side of relationship in JPA ? why don't we persist the other side of relation Hibernate @ManyToMany在插入的拥有侧插入0 - Hibernate @ManyToMany inserts 0 on the owning side of an insert
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM