检索JPA所有引用的关系(ManyToMany - ManyToOne - OneToMany)

时间:2018-02-28 10:52:50

标签: java jpa java-ee

使用此“推特”应用,用户可以在其中发布帖子@OneToMany并且可以拥有关注者@ManyToMany

在检索用户时,所有帖子和关注者也会被检索。

这一切都是正确的,但它也检索每个帖子(用户本身)的每个“海报”以及每个关注者,所有帖子和关注者。

我无法弄清楚如何将此限制为用户自己。

用户

@Entity
@Table(name = "User")
@NamedQueries({
  @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
  @NamedQuery(
    name = "User.auth",
    query = "SELECT u FROM User u WHERE u.username = :username AND u.password = :password"
  )
})
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(unique = true, nullable = false)
  private String username;

  @Column(nullable = false)
  private String password;

  @ManyToMany
  @OneToMany(mappedBy = "poster", cascade = CascadeType.ALL)
  private List<Post> posts = new ArrayList<>();

  @JoinTable(name = "Followers",
    joinColumns = {
      @JoinColumn(name = "USER_ID", referencedColumnName = "ID")
    },
    inverseJoinColumns = {
      @JoinColumn(name = "FOLLOWER_ID", referencedColumnName = "ID")
    }
  )
  private List<User> followers = new ArrayList<>();
 .... constructor, getters and setters

发表

@Entity
@Table(name = "Post")
public class Post {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String content;

  @ManyToOne
  private User poster;
  .... constructor, getters and setters

结果我得到了我想要的东西

{
    "id": 1,
    "username": "jim",
    "posts": [
        {
            "id": 1,
            "content": "Post 1 by jim",
            "poster": {
            // ^ this is the user itself (I don't need this one)
              "id": 1,
              "username": "jim",
              "posts": [
                // keeps recurse
              ]
            }
        }
    ],
    "followers": [
        {
            "id": 2,
            "username": "follower1",
            "posts": [
                {
                    "id": 4,
                    "content": "Post 2 by follower 1",
                    "poster": {
                      // ^ this is the follower itself (I don't need this one)
                      "id": 2,
                      "username": "follower1",
                      "posts": [
                        // Same issue
                      ]
                    }
                }
            ],
            "followers": [],    // <-- I don't need this one either
        }
    ]
}

很明显,获取一个用户填充会继续获取所有递归的关系。

这是设计师的错还是可以忽略/限制?

注意:我正在使用Gson将对象序列化为JSON格式

更新

试图使用:

@ManyToOne(fetch = FetchType.LAZY)
private User poster;

哪个有效,但仍然在JSON中得到以下额外支柱,不确定这是否是一个neath解决方案:

"_persistence_poster_vh": {
                "sourceAttributeName": "poster",
                "isInstantiated": false,
                "row": {
                    "Post.ID": 3,
                    "Post.CONTENT": "Post 3 by jim",
                    "Post.DATETIME": "2018-01-22",
                    "Post.POSTER_ID": 1
                },
                "isCoordinatedWithProperty": false
            }

  @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(
    ...
  )
  private List<User> followers = new ArrayList<>();

仍然会返回所有关注者(我想要的!)我只是不想追随者。追随者和追随者。帖子..

3 个答案:

答案 0 :(得分:1)

您可以在不想立即获取的注释中指定fetch=FetchType.LAZY。缺点是,如果您以后需要数据,则必须在仍然打开的会话范围内访问它。

答案 1 :(得分:1)

最佳猜测:在您尝试取消引用它们之前,它实际上并未获取这些对象。

默认情况下,JPA会急切地获取@OneToOne和@OneToMany关系,但不是@ManyToOne或@ManyToMany。会发生的是,当您引用这些字段时,它将转到并获取列表的实际内容。

你怎么知道这种情况发生了?使用getFollowers().getClass()检查列表的类 你看到的不是LinkedList或ArrayList,而是来自你的JPA提供者的类,可能在名称的某处有“Lazy”。当您在列表上调用Size或Get时,它将执行提取。

您也可以将OneToOne和OneToMany关系设置为惰性,并使用EntityGraphs确定您想要获取的实体。 JPA有很多类似的问题。

我看过GSON提到了,只是一个警告:它不会知道延迟加载列表,所以你必须告诉它以避免你不希望它遵循的属性。

通常使用JSON编组,您将希望它忽略父对象,因此在Post中,应该忽略User。此外,通常应忽略相同类型的链接(跟随者)或者特别映射,以便它不会对整个对象进行Marshall,而只生成一个用户名数组。您可以告诉它忽略实际的关注者字段,并让它编组一个getter,它返回一组用户名来实现它。

答案 2 :(得分:-1)

有两种方法可以解决这个问题 -

  1. 您可以在序列化对象时对要跳过的属性使用@JsonIgnoreProperties(ignoreUnknown=true) anotation。

  2. 或者您将FetchType更改为FetchType.LAZY,以便在准备JSON时可以根据需要获取所需数据,而不是一次性获取所有记录。

    < / LI>