JPA 在每次迭代后开始消耗越来越多的内存

Bie*_*bar 5 java jpa spring-data-jpa

目前我尝试在 JPA 的帮助下从 web api 存储一些新闻。我需要存储 3 个实体:网页、新闻帖子和返回新闻帖子的查询。我为三人各准备了一张桌子。我的简化 JPA 实体如下所示:

@Entity
@Data
@Table(name = "NewsPosts", schema = "data")
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class NewsPost {

    @Id
    @Column(name = "id")
    private long id;
    @Basic
    @Column(name = "subject")
    private String subject;
    @Basic
    @Column(name = "post_text")
    private String postText;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
    @JoinColumn(name = "newsSite")
    private NewsSite site;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
    @JoinTable(name = "query_news_post", joinColumns = @JoinColumn(name = "newsid"), inverseJoinColumns = @JoinColumn(name = "queryid"))
    private Set<QueryEntity> queries;
}


@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "queries", schema = "data")
@EqualsAndHashCode
public class QueryEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @EqualsAndHashCode.Exclude
    @Basic
    @Column(name = "query")
    private String query;

    // needs to be exclueded otherwise we can create stack overflow, because of circular references...
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    @ManyToMany(mappedBy = "queries", fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
    Set<PostsEntity> posts;

}



@Entity
@Data
@Table(name = "sites", schema = "data")
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class newsSite {
    @Id
    @Column(name = "SiteId")
    private long id;
    @Basic
    @Column(name = "SiteName")
    private String site;

}
Run Code Online (Sandbox Code Playgroud)

目前我正在执行以下操作:我创建查询并检索查询的 。然后我开始爬行:我以分页方式从 web api 中获取对象,页面大小为 100 个 newsPost 我使用对象映射器将 json 响应映射到我的实体类。

之后我尝试了两种不同的薄:

  1. 我将查询 ID 作为 Set 添加到 NewsPost 并使用EntityManager's merge 选项将其写回 DB 。这很有效,直到我到了这一点,我再次为另一个查询获得了一个 NewsPost,然后新查询被旧查询覆盖。为了解决这个问题,它尝试了 2。
  2. 我检查 NewsPost 是否已经存在我检索了该帖子,将新查询添加到现有查询并将其合并回数据库,就像我以前所做的那样。这样做时,我工作得很好,我得到了第一批的预期结果,但突然间,应用程序开始为第三批消耗越来越多的内存。我附上了一张来自 JavaVisualVM 的截图。有人知道为什么会这样吗?可视化虚拟机

编辑:由于评论中提出了一些问题,我想在这里提供问题的答案。

我认为爬行一切正常。Webapi 的返回以 json 形式出现。我正在使用 jackson 映射器将其映射到 POJO,然后我使用 Dozer 映射器将 POJO 转换为实体。(是的,为了应用程序中的其他目的,我首先需要执行 POJO 步骤,这很好用)。

关于 EntityManager 的写作,我不确定我是否正确地这样做。

起初,我创建了一个 JPA 存储库来检查帖子是否已经存在(以获取旧的查询 ID 并避免在 queryid、postid 表中覆盖的问题)。我的 JPA 存储库如下所示。

@Repository
public interface PostRepo extends JpaRepository<NewsPost, Long> {

    NewsPost getById(long id);
}
Run Code Online (Sandbox Code Playgroud)

要更新帖子,我按如下方式执行此操作:

private void updatePosts(List<NewsPost> posts){
    posts.forEach(post->{
                NewsPost foundPost = postRepo.getById(post.getId());
                if(foundPost!=null){
                    post.getQueries().addAll(foundPost.getQueries());   
                }});
}

Run Code Online (Sandbox Code Playgroud)

我目前正在编写我的实体,如下所示我有一个实体列表,其中还包含更新的帖子,并且EntityManagerFactory我的班级中有一个自动装配的处理写作。

EntityManager em = entityManagerFactory.createEntityManager();
        try {
            EntityTransaction transaction = em.getTransaction();
            transaction.begin();
            entities.forEach(entity->em.merge(entity))
            em.flush();
            transaction.commit();
        } finally {
            em.clear();
            em.close();
        }

Run Code Online (Sandbox Code Playgroud)

我很确定这是写作过程。如果我保持我的软件的逻辑相同但只跳过合并或只是将实体打印或转储到文件中,一切正常并且快速并且没有出现错误,所以它似乎是合并注释的问题?

关于我的程序是否因为它所依赖的内存消耗而死亡的问题。如果我在我的 mac 上运行它会消耗多达 8+ GB 的内存,但 MAC OS 正在处理这个并将内存交换到磁盘。如果我将它作为 docker 容器 von CentOS 运行,则由于内存较少,该进程将被终止。

如果这是相关的,现在不要,但我使用的是 OpenJDK 11、Springboot 2.2.6 和 MYSQL 8 数据库。

我在 application.yml 中配置 jpa 如下:

spring:
  main:
    allow-bean-definition-overriding: true
  datasource:
    url: "jdbc:mysql://db"
    username: user
    password: secret
    driver-class-name: com.mysql.cj.jdbc.Driver
    test-while-idle: true
    validation-query: Select 1
  jpa:
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        event:
          merge:
            entity_copy_observer: allow
    ```
Run Code Online (Sandbox Code Playgroud)

Bie*_*bar 1

我自己尝试解决了。我为多对多关系创建了一个实体。之后,我为每个实体创建了 CRUD 存储库,并从 CRUD 存储库中使用saveAll。这对于内存也工作得很好。GC 现在在内存可视化中生成预期的电锯图案。但我仍然不知道为什么我之前使用注释中的联接表创建的多对多关系会产生有关内存管理的问题。有人可以解释为什么这解决了我的问题是 ManyToMany 创建循环依赖关系吗?但据我所知GC也发现了循环依赖。