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

Java JPA检索所有引用的关系(ManyToMany-ManyToOne-OneToMany),java,jpa,jakarta-ee,Java,Jpa,Jakarta Ee,在这个“twitter”应用程序中,用户可以拥有帖子@OneToMany,也可以拥有追随者@manytomy 在检索用户时,也会检索其所有帖子和关注者 这一切都是正确的,但它也检索每个帖子(即用户本身)和每个关注者的每个“海报”,所有帖子和关注者 我不知道如何将此限制到用户本身 用户 @Entity @Table(name = "User") @NamedQueries({ @NamedQuery(name = "User.findAll", query = "SELECT u FROM U

在这个“twitter”应用程序中,用户可以拥有帖子
@OneToMany
,也可以拥有追随者
@manytomy

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

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

我不知道如何将此限制到用户本身

用户

@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
我得到的结果vs我想要的

@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
        }
    ]
}
它可以工作,但仍然在JSONS中获得以下额外的支持,因此不确定这是否是一个最新的解决方案:

"_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)
@可接合(
...
)
private List followers=new ArrayList();

仍然返回所有追随者(我想要!)我只是不想要followers.followers和followers.posts..

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

有两种方法来处理此问题-

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

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


  • 最佳猜测:在您尝试取消引用这些对象之前,它实际上不会获取这些对象

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

    你怎么知道这是怎么回事?使用
    getFollowers().getClass()
    您看到的不是LinkedList或ArrayList,而是来自您的JPA提供者的类,名称中可能有“Lazy”。当您调用Size或Get进入列表时,它将执行提取

    您还可以将OneToOne和OneToMany关系设置为惰性,并使用EntityGraphs来确定您想要急切获取的实体。JPA有很多这样的陷阱

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


    通常,在JSON封送处理中,您希望它忽略父对象,因此在Post中,用户应该被忽略。此外,到相同类型的链接通常应该被忽略(followers)或专门映射,这样它就不会对整个对象进行马歇尔处理,而只生成一个用户名数组。您可以告诉它忽略实际的followers字段,并让它封送一个getter,该getter返回一个用户名数组来实现这一点。

    请参阅上的答案Henry@JimVercoelen我可以删除第二点,但第一点仍然建议采用另一种方法来处理该案件。因此,请随意编辑答案,而不是否决投票。请参阅Henry上的答案您更新了答案。您在GSON中提到忽略它,而不是提取本身。我已经想到了这个,但是如果我不需要的话,获取所有实体及其所有关系,然后用JSON转换器忽略它们,这不是效率低下吗?确实是,但一般来说,JPA只急切地获取基数1关系,而不是每个关系的整个列表,因此,开销比您想象的要小得多。这也不是问题的根源。基本上,您遇到的问题似乎是对JPA的延迟加载语义缺乏了解,以及它如何与JSON封送等交互,而不是对实体缺乏适当的延迟加载语义。关于包含GSON详细信息的更新,您最初的问题中没有提到这一点,所以我本来没有谈过。当我注意到你正在整理实体图时,你的问题变得非常清楚。那么,我怎么会让每个User.followers.followers和User.followers.posts不断递归呢?我在记录列表类名时检查了它,它确实是一个JPA列表类型。所以问题是跟随者调用的转换器会在轮到它的时候被获取,或者?我更新了我的问题。在Post.poster上使用fetch=FetchType.LAZY即可。但是在User.followers上使用它仍然会返回followers(我需要)。我只是不想要User.followers.followers和User.followers.posts。
    "_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<>();